Browse Source

Add spliffy framework (#6697)

* add spliffy tests impl
verify not passing yet

* All tests working, benchmarks all run and pass verification

* All tests working, benchmarks all run and pass verification

* platform and webserver?

* platform is node?

* this feels right

* newlines make the world go 'round

* query max connections from db
only use 90% of max connections to avoid too many client errors

* close the connection after getting max_connections

* only init the workers
snowbldr 4 years ago
parent
commit
5282196ce4

+ 4 - 0
frameworks/JavaScript/spliffy/.dockerignore

@@ -0,0 +1,4 @@
+node_modules
+benchmark_config.json
+README.md
+spliffy.dockerfile

+ 48 - 0
frameworks/JavaScript/spliffy/README.md

@@ -0,0 +1,48 @@
+
+
+
+# spliffy Benchmarking Test
+
+### Test Type Implementation Source Code
+
+* [JSON](www/json.rt.js)
+* [PLAINTEXT](www/plaintext.rt.js)
+* [DB](www/db.rt.js)
+* [QUERY](www/queries.rt.js)
+* [CACHED QUERY](www/cached-worlds.rt.js)
+* [UPDATE](www/updates.rt.js)
+* [FORTUNES](www/fortunes.rt.js)
+
+## Important Libraries
+The tests were run with:
+* <a href="https://srfnstack.github.io/spliffy/" target="_blank">Spliffy</a>
+* <a href="https://github.com/uNetworking/uWebSockets.js" target="_blank">μWebSockets.js</a>
+
+## Test URLs
+### JSON
+
+http://localhost:1420/json
+
+### PLAINTEXT
+
+http://localhost:1420/plaintext
+
+### DB
+
+http://localhost:1420/db
+
+### QUERY
+
+http://localhost:1420/queries?queries=
+
+### CACHED QUERY
+
+http://localhost:1420/cached-worlds?count=
+
+### UPDATE
+
+http://localhost:1420/updates?queries=
+
+### FORTUNES
+
+http://localhost:1420/fortunes

+ 31 - 0
frameworks/JavaScript/spliffy/benchmark_config.json

@@ -0,0 +1,31 @@
+{
+  "framework": "spliffy",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "cached_query_url": "/cached-worlds?count=",
+        "update_url": "/updates?queries=",
+        "fortune_url": "/fortunes",
+        "port": 1420,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "postgres",
+        "framework": "spliffy",
+        "language": "Javascript",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Node.js",
+        "webserver": "µws",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "spliffy",
+        "notes": "directory based routing for node",
+        "versus": "nodejs"
+      }
+    }
+  ]
+}

+ 86 - 0
frameworks/JavaScript/spliffy/db.js

@@ -0,0 +1,86 @@
+const { Pool, native } = require( 'pg' );
+const cpus = require( 'os' ).cpus().length
+let maxConnections
+let clientOpts = {
+    Client: native.Client,
+    host: 'tfb-database',
+    // host: 'host.docker.internal',
+    // host: 'localhost',
+    user: 'benchmarkdbuser',
+    password: 'benchmarkdbpass',
+    database: 'hello_world'
+};
+
+let pool
+
+let execute = async ( text, values ) => {
+    try {
+        return ( await pool.query( text, values || undefined ) ).rows;
+    } catch( e ) {
+        throw new Error( e )
+    }
+}
+module.exports = {
+    async init() {
+        const client = new native.Client( clientOpts )
+        await client.connect()
+        const res = await client.query( 'SHOW max_connections' )
+        maxConnections = res.rows[0].max_connections
+        //1 worker per cpu, each worker pool gets a fraction of the max connections
+        //only use 90% to avoid too many clients errors
+        pool = new Pool( Object.assign( { ...clientOpts }, { max: Math.floor( maxConnections * 0.9 / cpus ) } ) )
+        pool.on( 'error', ( err ) => {
+            console.error( 'Unexpected client error', err )
+        } )
+        await client.end()
+    },
+    execute,
+    randomId: () => Math.floor( Math.random() * 10000 ) + 1,
+    randomUniqueIds: ( count ) => {
+        const ids = {}
+        for( let i = 0; i < count; i++ ) {
+            let id = module.exports.randomId()
+            if( ids[id] ) {
+                for( let j = 0; j < 10000 - 1; j++ ) {
+                    if( !ids[id] ) break
+                    id++
+                    if( id > 10000 ) {
+                        id = 1
+                    }
+                }
+            }
+            ids[id] = true
+        }
+        return Object.keys( ids )
+    },
+
+    allFortunes: async () =>
+        execute( 'SELECT * FROM fortune' ),
+
+    worldById: async ( id ) =>
+        execute( `SELECT *
+                  FROM world
+                  WHERE id = $1`, [id] )
+            .then( arr => arr[0] ),
+
+    allWorlds: async () =>
+        execute( 'SELECT * FROM world' ),
+
+    bulkUpdateWorld: async worlds =>
+        execute(
+            `UPDATE world as w
+             SET randomnumber = wc.randomnumber
+             FROM (
+                      SELECT win.id, win.randomnumber
+                      FROM world wb,
+                           (VALUES ${
+                                   //0 -> 1,2 ; 1 -> 3,4; 2 -> 5,6; 3 -> 7,8 ... = (i+1) * 2 - 1, (i+1) * 2
+                                   worlds.map( ( _, i ) => `(\$${( i + 1 ) * 2 - 1}::int,$${( i + 1 ) * 2}::int)` ).join( ',' )
+                           }) AS win (id, randomnumber)
+                      WHERE wb.id = win.id
+                          FOR UPDATE
+                  ) as wc
+             where w.id = wc.id`,
+            worlds.map( world => [world.id, world.randomnumber] ).flat() )
+            .then( () => worlds )
+}

+ 3 - 0
frameworks/JavaScript/spliffy/fn.js

@@ -0,0 +1,3 @@
+module.exports = {
+    parseCount: ( i ) => Math.min( Math.max( parseInt( i, 10 ) || 1, 1 ), 500 )
+}

+ 271 - 0
frameworks/JavaScript/spliffy/package-lock.json

@@ -0,0 +1,271 @@
+{
+  "name": "spliffy-benchmark",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@srfnstack/spliffy": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/@srfnstack/spliffy/-/spliffy-0.6.1.tgz",
+      "integrity": "sha512-uXqHMt8hHMZmkp+Ve9ou5pvTrTZOI270G50cgVDxha5rXu9Eki+7TvPvO20p7jM7lxG0BkYoEHxQhrD+kNMXbw==",
+      "requires": {
+        "cookie": "^0.4.0",
+        "etag": "^1.8.1",
+        "uWebSockets.js": "github:uNetworking/uWebSockets.js#v19.3.0",
+        "uuid": "^3.3.3"
+      }
+    },
+    "bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "requires": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
+    "buffer-writer": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
+      "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="
+    },
+    "clone": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+      "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18="
+    },
+    "cookie": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+      "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
+    },
+    "core-util-is": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+    },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+    },
+    "file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
+    },
+    "html-escaper": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
+      "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+    },
+    "libpq": {
+      "version": "1.8.9",
+      "resolved": "https://registry.npmjs.org/libpq/-/libpq-1.8.9.tgz",
+      "integrity": "sha512-herU0STiW3+/XBoYRycKKf49O9hBKK0JbdC2QmvdC5pyCSu8prb9idpn5bUSbxj8XwcEsWPWWWwTDZE9ZTwJ7g==",
+      "requires": {
+        "bindings": "1.5.0",
+        "nan": "^2.14.0"
+      }
+    },
+    "nan": {
+      "version": "2.14.2",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
+      "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
+    },
+    "node-cache": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz",
+      "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==",
+      "requires": {
+        "clone": "2.x"
+      }
+    },
+    "packet-reader": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
+      "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
+    },
+    "pg": {
+      "version": "8.6.0",
+      "resolved": "https://registry.npmjs.org/pg/-/pg-8.6.0.tgz",
+      "integrity": "sha512-qNS9u61lqljTDFvmk/N66EeGq3n6Ujzj0FFyNMGQr6XuEv4tgNTXvJQTfJdcvGit5p5/DWPu+wj920hAJFI+QQ==",
+      "requires": {
+        "buffer-writer": "2.0.0",
+        "packet-reader": "1.0.0",
+        "pg-connection-string": "^2.5.0",
+        "pg-pool": "^3.3.0",
+        "pg-protocol": "^1.5.0",
+        "pg-types": "^2.1.0",
+        "pgpass": "1.x"
+      }
+    },
+    "pg-connection-string": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
+      "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ=="
+    },
+    "pg-int8": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+      "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
+    },
+    "pg-native": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pg-native/-/pg-native-3.0.0.tgz",
+      "integrity": "sha512-qZZyywXJ8O4lbiIN7mn6vXIow1fd3QZFqzRe+uET/SZIXvCa3HBooXQA4ZU8EQX8Ae6SmaYtDGLp5DwU+8vrfg==",
+      "requires": {
+        "libpq": "^1.7.0",
+        "pg-types": "^1.12.1",
+        "readable-stream": "1.0.31"
+      },
+      "dependencies": {
+        "pg-types": {
+          "version": "1.13.0",
+          "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.13.0.tgz",
+          "integrity": "sha512-lfKli0Gkl/+za/+b6lzENajczwZHc7D5kiUCZfgm914jipD2kIOIvEkAhZ8GrW3/TUoP9w8FHjwpPObBye5KQQ==",
+          "requires": {
+            "pg-int8": "1.0.1",
+            "postgres-array": "~1.0.0",
+            "postgres-bytea": "~1.0.0",
+            "postgres-date": "~1.0.0",
+            "postgres-interval": "^1.1.0"
+          }
+        },
+        "postgres-array": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.3.tgz",
+          "integrity": "sha512-5wClXrAP0+78mcsNX3/ithQ5exKvCyK5lr5NEEEeGwwM6NJdQgzIJBVxLvRW+huFpX92F2QnZ5CcokH0VhK2qQ=="
+        },
+        "readable-stream": {
+          "version": "1.0.31",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.31.tgz",
+          "integrity": "sha1-jyUC4LyeOw2huUUgqrtOJgPsr64=",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.1",
+            "isarray": "0.0.1",
+            "string_decoder": "~0.10.x"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+        }
+      }
+    },
+    "pg-pool": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.3.0.tgz",
+      "integrity": "sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg=="
+    },
+    "pg-protocol": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz",
+      "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ=="
+    },
+    "pg-types": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+      "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+      "requires": {
+        "pg-int8": "1.0.1",
+        "postgres-array": "~2.0.0",
+        "postgres-bytea": "~1.0.0",
+        "postgres-date": "~1.0.4",
+        "postgres-interval": "^1.1.0"
+      }
+    },
+    "pgpass": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.4.tgz",
+      "integrity": "sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==",
+      "requires": {
+        "split2": "^3.1.1"
+      }
+    },
+    "postgres-array": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+      "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="
+    },
+    "postgres-bytea": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
+      "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU="
+    },
+    "postgres-date": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+      "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="
+    },
+    "postgres-interval": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+      "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+      "requires": {
+        "xtend": "^4.0.0"
+      }
+    },
+    "readable-stream": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+      "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+      "requires": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+    },
+    "split2": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
+      "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
+      "requires": {
+        "readable-stream": "^3.0.0"
+      }
+    },
+    "string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "requires": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "uWebSockets.js": {
+      "version": "github:uNetworking/uWebSockets.js#7bf0faac5859fef2d113e83d22803f7833774c11",
+      "from": "github:uNetworking/uWebSockets.js#v19.3.0"
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+    },
+    "uuid": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
+    },
+    "xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+    }
+  }
+}

+ 12 - 0
frameworks/JavaScript/spliffy/package.json

@@ -0,0 +1,12 @@
+{
+  "name": "spliffy-benchmark",
+  "version": "1.0.0",
+  "main": "start.js",
+  "dependencies": {
+    "@srfnstack/spliffy": "0.6.1",
+    "html-escaper": "3.0.3",
+    "node-cache": "5.1.2",
+    "pg": "8.6.0",
+    "pg-native": "3.0.0"
+  }
+}

+ 11 - 0
frameworks/JavaScript/spliffy/spliffy.dockerfile

@@ -0,0 +1,11 @@
+FROM node:14-buster-slim
+
+RUN apt update && apt install -y libpq-dev g++ make git python
+COPY package.json /app/
+WORKDIR /app
+RUN npm install
+COPY . /app
+
+EXPOSE 1420
+
+CMD ["node", "/app/start.js"]

+ 21 - 0
frameworks/JavaScript/spliffy/start.js

@@ -0,0 +1,21 @@
+const cluster = require( 'cluster' )
+const cpus = require( 'os' ).cpus().length
+const db = require( './db' )
+
+if( cluster.isMaster ) {
+    for( let i = 0; i < cpus; i++ ) {
+        cluster.fork();
+    }
+} else {
+    db.init()
+        .then( () => {
+                require( '@srfnstack/spliffy' )(
+                    {
+                        routeDir: __dirname + '/www',
+                        port: 1420,
+                        logAccess: false
+                    }
+                )
+            }
+        )
+}

+ 12 - 0
frameworks/JavaScript/spliffy/www/cached-worlds.rt.js

@@ -0,0 +1,12 @@
+const NodeCache = require( 'node-cache' )
+const worldCache = new NodeCache()
+const db = require( '../db' )
+const { parseCount } = require( '../fn' )
+
+db.allWorlds().then( worlds => worlds.forEach( world => worldCache.set( world.id, world ) ) )
+
+module.exports = {
+    GET: ( { url: { query: { count } } } ) =>
+        db.randomUniqueIds( parseCount( count ) )
+            .map( id => worldCache.get( id ) )
+}

+ 5 - 0
frameworks/JavaScript/spliffy/www/db.rt.js

@@ -0,0 +1,5 @@
+const db = require( '../db' )
+
+module.exports = {
+    GET: () => db.worldById( db.randomId() )
+}

+ 27 - 0
frameworks/JavaScript/spliffy/www/fortunes.rt.js

@@ -0,0 +1,27 @@
+const db = require( '../db' )
+const { escape } = require( 'html-escaper' )
+const renderFortunes = fortunes => {
+    let rendered = '<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>'
+    for( let i = 0; i < fortunes.length; i++ ) {
+        rendered += `<tr><td>${fortunes[i].id}</td><td>${escape( fortunes[i].message )}</td></tr>`
+    }
+    rendered += '</table></body></html>'
+    return rendered
+}
+
+module.exports = {
+    GET: async () => ( {
+        headers: {
+            'Content-Type': 'text/html; charset=utf-8'
+        },
+        body: renderFortunes(
+            [
+                ...await db.allFortunes(),
+                {
+                    id: 0,
+                    message: 'Additional fortune added at request time.'
+                }
+            ].sort( ( a, b ) => a.message.localeCompare( b.message ) )
+        )
+    } )
+}

+ 3 - 0
frameworks/JavaScript/spliffy/www/json.rt.js

@@ -0,0 +1,3 @@
+module.exports = {
+    GET: () => ( { message: 'Hello, World!' } )
+}

+ 6 - 0
frameworks/JavaScript/spliffy/www/plaintext.rt.js

@@ -0,0 +1,6 @@
+module.exports = {
+    GET: ( { res } ) => {
+        res.headers['content-type'] = 'text/plain'
+        return 'Hello, World!'
+    }
+}

+ 8 - 0
frameworks/JavaScript/spliffy/www/queries.rt.js

@@ -0,0 +1,8 @@
+const db = require( '../db' )
+const { parseCount } = require( '../fn' )
+module.exports = {
+    GET: async ( { url: { query: { queries } } } ) => await Promise.all(
+        db.randomUniqueIds( parseCount( queries ) )
+            .map( id => db.worldById( id ) )
+    )
+}

+ 16 - 0
frameworks/JavaScript/spliffy/www/updates.rt.js

@@ -0,0 +1,16 @@
+const db = require( '../db' )
+const { parseCount } = require( '../fn' )
+module.exports = {
+    GET: async ( { url: { query: { queries } } } ) => await db.bulkUpdateWorld(
+        await Promise.all(
+            db.randomUniqueIds( parseCount( queries ) )
+                .map( id =>
+                    db.worldById( id )
+                        .then( world => {
+                            world.randomnumber = db.randomId()
+                            return world
+                        } )
+                )
+        )
+    )
+}

+ 3 - 1
package.json

@@ -1,5 +1,7 @@
 {
 {
   "dependencies": {
   "dependencies": {
-    "hbs": "^4.0.4"
+    "hbs": "^4.0.4",
+    "pg": "^8.6.0",
+    "pg-native": "^3.0.0"
   }
   }
 }
 }