Browse Source

Add db impls to spliffy (#6730)

* add mysql and mongodb database impls

* don't load worlds without db

* fix mysql verify

* fix mongo
cleanup a bit

* use db.getSiblingDB in mongo create.js because use hell_world doesn't work (at least not on windows)

* it's leviOsa

* revert use db.getSiblingDb

* apparently using the native pool breaks bulk updates

* pool gets a connection per statement already

* remove whitespace from query
snowbldr 4 years ago
parent
commit
f5f167d475

+ 1 - 1
frameworks/JavaScript/spliffy/.dockerignore

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

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

@@ -1,6 +1,3 @@
-
-
-
 # spliffy Benchmarking Test
 # spliffy Benchmarking Test
 
 
 ### Test Type Implementation Source Code
 ### Test Type Implementation Source Code

+ 63 - 2
frameworks/JavaScript/spliffy/benchmark_config.json

@@ -5,6 +5,67 @@
       "default": {
       "default": {
         "json_url": "/json",
         "json_url": "/json",
         "plaintext_url": "/plaintext",
         "plaintext_url": "/plaintext",
+        "port": 1420,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "None",
+        "framework": "spliffy",
+        "language": "Javascript",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "NodeJS",
+        "webserver": "µws",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "spliffy",
+        "notes": "directory based routing for node",
+        "versus": "nodejs"
+      },
+      "mongodb": {
+        "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": "MongoDB",
+        "framework": "spliffy",
+        "language": "Javascript",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "NodeJS",
+        "webserver": "µws",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "spliffy",
+        "notes": "directory based routing for node",
+        "versus": "nodejs"
+      },
+      "mysql": {
+        "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": "MySQL",
+        "framework": "spliffy",
+        "language": "Javascript",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "NodeJS",
+        "webserver": "µws",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "spliffy",
+        "notes": "directory based routing for node",
+        "versus": "nodejs"
+      },
+      "postgres": {
         "db_url": "/db",
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "query_url": "/queries?queries=",
         "cached_query_url": "/cached-worlds?count=",
         "cached_query_url": "/cached-worlds?count=",
@@ -13,12 +74,12 @@
         "port": 1420,
         "port": 1420,
         "approach": "Realistic",
         "approach": "Realistic",
         "classification": "Micro",
         "classification": "Micro",
-        "database": "postgres",
+        "database": "Postgres",
         "framework": "spliffy",
         "framework": "spliffy",
         "language": "Javascript",
         "language": "Javascript",
         "flavor": "None",
         "flavor": "None",
         "orm": "Raw",
         "orm": "Raw",
-        "platform": "Node.js",
+        "platform": "NodeJS",
         "webserver": "µws",
         "webserver": "µws",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",

+ 16 - 82
frameworks/JavaScript/spliffy/db.js

@@ -1,86 +1,20 @@
-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 dbImpl
 
 
-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
-                    }
-                }
+switch( process.env.DATABASE ) {
+    case 'mysql':
+        dbImpl = require( './db/mysql' )
+        break
+    case 'mongodb':
+        dbImpl = require( './db/mongodb' )
+        break
+    case 'postgres':
+        dbImpl = require( './db/postgres' )
+        break
+    default:
+        dbImpl = {
+            init: async () => {
             }
             }
-            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 )
 }
 }
+
+module.exports = dbImpl

+ 36 - 0
frameworks/JavaScript/spliffy/db/mongodb.js

@@ -0,0 +1,36 @@
+const { MongoClient } = require( "mongodb" );
+
+let World, Fortune;
+
+const dbHost = process.env.DB_HOST || 'localhost'
+
+const projection = { projection: { _id: 0 } }
+
+module.exports = {
+    async init() {
+        let client = new MongoClient(`mongodb://${dbHost}:27017`, { minPoolSize: 2, maxPoolSize: 75 })
+        await client.connect()
+        let db = await client.db( 'hello_world' );
+        Fortune = await db.collection( 'fortune' );
+        World = await db.collection( 'world' );
+    },
+    allFortunes: async () => Fortune.find( undefined, projection ).toArray(),
+
+    worldById: async ( id ) => World.findOne( { _id: id }, projection ),
+
+    allWorlds: async () => World.find( undefined, projection ).toArray(),
+
+    bulkUpdateWorld: async worlds =>
+        World.bulkWrite(
+            worlds.map( world => ( {
+                updateOne: {
+                    filter: {
+                        _id: world.id
+                    },
+                    update: {
+                        $set: { randomNumber: world.randomNumber }
+                    }
+                }
+            } ) )
+        ).then(()=>worlds)
+}

+ 40 - 0
frameworks/JavaScript/spliffy/db/mysql.js

@@ -0,0 +1,40 @@
+const { createPool, createConnection } = require( 'mysql2/promise' );
+const cpus = require( 'os' ).cpus().length
+
+let clientOpts = {
+    host: process.env.DB_HOST || 'localhost',
+    user: 'benchmarkdbuser',
+    password: 'benchmarkdbpass',
+    database: 'hello_world'
+};
+
+let pool
+
+const execute = async ( text, values ) => ( await pool.execute( text, values || undefined ) )[0]
+
+module.exports = {
+    async init() {
+        const client = await createConnection( clientOpts )
+        const res = await client.query( 'SHOW VARIABLES LIKE "max_connections"' )
+        let maxConnections = Math.floor( res[0][0].Value * 0.9 / cpus )
+        //1 worker per cpu, each worker pool gets a fraction of the max connections
+        //only use 90% to avoid too many clients errors
+        pool = createPool( Object.assign( { ...clientOpts }, { max: maxConnections } ) )
+        await client.end()
+    },
+    allFortunes: async () =>
+        execute( 'SELECT * FROM fortune' ),
+
+    worldById: async ( id ) =>
+        execute( 'SELECT * FROM world WHERE id = ?', [id] )
+            .then( arr => arr[0] ),
+
+    allWorlds: async () =>
+        execute( 'SELECT * FROM world' ),
+
+    bulkUpdateWorld: async worlds => Promise.all(
+        worlds.map( world =>
+            execute( 'UPDATE world SET randomnumber = ? WHERE id = ?',
+                [world.randomnumber, world.id] ) )
+    ).then( () => worlds )
+}

+ 44 - 0
frameworks/JavaScript/spliffy/db/postgres.js

@@ -0,0 +1,44 @@
+const { Client, Pool } = require( 'pg' ).native;
+const cpus = require( 'os' ).cpus().length
+
+let clientOpts = {
+    host: process.env.DB_HOST || 'localhost',
+    user: 'benchmarkdbuser',
+    password: 'benchmarkdbpass',
+    database: 'hello_world'
+};
+
+let pool
+
+const query = async ( text, values ) => ( await pool.query( text, values || undefined ) ).rows;
+
+module.exports = {
+    async init() {
+        const client = new Client( clientOpts )
+        await client.connect()
+        const res = await client.query( 'SHOW max_connections' )
+        let maxConnections = Math.floor( res.rows[0].max_connections * 0.9 / cpus )
+        //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: maxConnections } ) )
+        pool.on( 'error', ( err ) => {
+            console.error( 'Unexpected client error', err )
+        } )
+        await client.end()
+    },
+    allFortunes: async () =>
+        query( 'SELECT * FROM fortune' ),
+
+    worldById: async ( id ) =>
+        query( 'SELECT * FROM world WHERE id = $1', [id] )
+            .then( arr => arr[0] ),
+
+    allWorlds: async () =>
+        query( 'SELECT * FROM world' ),
+
+    bulkUpdateWorld: async worlds => Promise.all(
+        worlds.map( world =>
+            query( 'UPDATE world SET randomnumber = $1 WHERE id = $2',
+                [world.randomnumber, world.id] ) )
+    ).then( () => worlds )
+}

+ 21 - 1
frameworks/JavaScript/spliffy/fn.js

@@ -1,3 +1,23 @@
 module.exports = {
 module.exports = {
-    parseCount: ( i ) => Math.min( Math.max( parseInt( i, 10 ) || 1, 1 ), 500 )
+    parseCount: ( i ) => Math.min( Math.max( parseInt( i, 10 ) || 1, 1 ), 500 ),
+    randomId: () => Math.floor( Math.random() * 10000 ) + 1,
+    randomUniqueIds: ( count ) => {
+        const used = new Map()
+        const ids = []
+        for( let i = 0; i < count; i++ ) {
+            let id = module.exports.randomId()
+            if( used.has(id) ) {
+                for( let j = 0; j < 10000 - 1; j++ ) {
+                    if( !used.has(id) ) break
+                    id++
+                    if( id > 10000 ) {
+                        id = 1
+                    }
+                }
+            }
+            used.set(id, true)
+            ids.push(id)
+        }
+        return ids
+    },
 }
 }

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

@@ -15,6 +15,11 @@
         "uuid": "^3.3.3"
         "uuid": "^3.3.3"
       }
       }
     },
     },
+    "base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+    },
     "bindings": {
     "bindings": {
       "version": "1.5.0",
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
       "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@@ -23,6 +28,23 @@
         "file-uri-to-path": "1.0.0"
         "file-uri-to-path": "1.0.0"
       }
       }
     },
     },
+    "bson": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/bson/-/bson-4.4.1.tgz",
+      "integrity": "sha512-Uu4OCZa0jouQJCKOk1EmmyqtdWAP5HVLru4lQxTwzJzxT+sJ13lVpEZU/MATDxtHiekWMAL84oQY3Xn1LpJVSg==",
+      "requires": {
+        "buffer": "^5.6.0"
+      }
+    },
+    "buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+      "requires": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
     "buffer-writer": {
     "buffer-writer": {
       "version": "2.0.0",
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
       "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
@@ -43,6 +65,11 @@
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
     },
     },
+    "denque": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.0.tgz",
+      "integrity": "sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ=="
+    },
     "etag": {
     "etag": {
       "version": "1.8.1",
       "version": "1.8.1",
       "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
       "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
@@ -53,16 +80,42 @@
       "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
       "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=="
       "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
     },
     },
+    "generate-function": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+      "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+      "requires": {
+        "is-property": "^1.0.2"
+      }
+    },
     "html-escaper": {
     "html-escaper": {
       "version": "3.0.3",
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
       "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz",
       "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="
       "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ=="
     },
     },
+    "iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      }
+    },
+    "ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
+    },
     "inherits": {
     "inherits": {
       "version": "2.0.4",
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
       "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
       "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     },
+    "is-property": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+      "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
+    },
     "isarray": {
     "isarray": {
       "version": "0.0.1",
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
@@ -77,6 +130,95 @@
         "nan": "^2.14.0"
         "nan": "^2.14.0"
       }
       }
     },
     },
+    "lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    },
+    "long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+    },
+    "lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "requires": {
+        "yallist": "^4.0.0"
+      }
+    },
+    "memory-pager": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+      "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+      "optional": true
+    },
+    "mongodb": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.0.1.tgz",
+      "integrity": "sha512-Ll2YCciRgbFN2jdfSqW1vhxvAcnqu+5ZlrTZNaEg+hZqKREg4xiUV56ZAtTjC02skfoTirHY5jQwtg7mBxqfug==",
+      "requires": {
+        "bson": "^4.4.0",
+        "denque": "^1.5.0",
+        "mongodb-connection-string-url": "^1.0.1",
+        "saslprep": "^1.0.0"
+      }
+    },
+    "mongodb-connection-string-url": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-1.0.1.tgz",
+      "integrity": "sha512-sXi8w9nwbMrErWbOK+8nofHz531rboasDbYAMS+sQ+W+2YnHN980RlMr+t5SDL6uKEU/kw/wG6jcjCTLiJltoA==",
+      "requires": {
+        "whatwg-url": "^8.4.0"
+      }
+    },
+    "mysql2": {
+      "version": "2.2.5",
+      "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.2.5.tgz",
+      "integrity": "sha512-XRqPNxcZTpmFdXbJqb+/CtYVLCx14x1RTeNMD4954L331APu75IC74GDqnZMEt1kwaXy6TySo55rF2F3YJS78g==",
+      "requires": {
+        "denque": "^1.4.1",
+        "generate-function": "^2.3.1",
+        "iconv-lite": "^0.6.2",
+        "long": "^4.0.0",
+        "lru-cache": "^6.0.0",
+        "named-placeholders": "^1.1.2",
+        "seq-queue": "^0.0.5",
+        "sqlstring": "^2.3.2"
+      },
+      "dependencies": {
+        "sqlstring": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.2.tgz",
+          "integrity": "sha512-vF4ZbYdKS8OnoJAWBmMxCQDkiEBkGQYU7UZPtL8flbDRSNkhaXvRJ279ZtI6M+zDaQovVU4tuRgzK5fVhvFAhg=="
+        }
+      }
+    },
+    "named-placeholders": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz",
+      "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==",
+      "requires": {
+        "lru-cache": "^4.1.3"
+      },
+      "dependencies": {
+        "lru-cache": {
+          "version": "4.1.5",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+          "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+          "requires": {
+            "pseudomap": "^1.0.2",
+            "yallist": "^2.1.2"
+          }
+        },
+        "yallist": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+          "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+        }
+      }
+    },
     "nan": {
     "nan": {
       "version": "2.14.2",
       "version": "2.14.2",
       "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
       "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
@@ -217,6 +359,16 @@
         "xtend": "^4.0.0"
         "xtend": "^4.0.0"
       }
       }
     },
     },
+    "pseudomap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
+    },
     "readable-stream": {
     "readable-stream": {
       "version": "3.6.0",
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@@ -232,6 +384,34 @@
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
       "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
       "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
     },
     },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "saslprep": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
+      "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
+      "optional": true,
+      "requires": {
+        "sparse-bitfield": "^3.0.3"
+      }
+    },
+    "seq-queue": {
+      "version": "0.0.5",
+      "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+      "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4="
+    },
+    "sparse-bitfield": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+      "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
+      "optional": true,
+      "requires": {
+        "memory-pager": "^1.0.2"
+      }
+    },
     "split2": {
     "split2": {
       "version": "3.2.2",
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
       "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
@@ -248,6 +428,14 @@
         "safe-buffer": "~5.2.0"
         "safe-buffer": "~5.2.0"
       }
       }
     },
     },
+    "tr46": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz",
+      "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==",
+      "requires": {
+        "punycode": "^2.1.1"
+      }
+    },
     "uWebSockets.js": {
     "uWebSockets.js": {
       "version": "github:uNetworking/uWebSockets.js#7bf0faac5859fef2d113e83d22803f7833774c11",
       "version": "github:uNetworking/uWebSockets.js#7bf0faac5859fef2d113e83d22803f7833774c11",
       "from": "github:uNetworking/uWebSockets.js#v19.3.0"
       "from": "github:uNetworking/uWebSockets.js#v19.3.0"
@@ -262,10 +450,30 @@
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
       "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
       "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
       "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
     },
     },
+    "webidl-conversions": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
+      "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w=="
+    },
+    "whatwg-url": {
+      "version": "8.7.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz",
+      "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==",
+      "requires": {
+        "lodash": "^4.7.0",
+        "tr46": "^2.1.0",
+        "webidl-conversions": "^6.1.0"
+      }
+    },
     "xtend": {
     "xtend": {
       "version": "4.0.2",
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
       "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
       "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+    },
+    "yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
     }
     }
   }
   }
 }
 }

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

@@ -5,6 +5,8 @@
   "dependencies": {
   "dependencies": {
     "@srfnstack/spliffy": "0.6.1",
     "@srfnstack/spliffy": "0.6.1",
     "html-escaper": "3.0.3",
     "html-escaper": "3.0.3",
+    "mongodb": "^4.0.1",
+    "mysql2": "^2.2.5",
     "node-cache": "5.1.2",
     "node-cache": "5.1.2",
     "pg": "8.6.0",
     "pg": "8.6.0",
     "pg-native": "3.0.0"
     "pg-native": "3.0.0"

+ 14 - 0
frameworks/JavaScript/spliffy/spliffy-mongodb.dockerfile

@@ -0,0 +1,14 @@
+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
+
+ENV DB_HOST=tfb-database
+ENV DATABASE=mongodb
+
+EXPOSE 1420
+
+CMD ["node", "/app/start.js"]

+ 14 - 0
frameworks/JavaScript/spliffy/spliffy-mysql.dockerfile

@@ -0,0 +1,14 @@
+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
+
+ENV DB_HOST=tfb-database
+ENV DATABASE=mysql
+
+EXPOSE 1420
+
+CMD ["node", "/app/start.js"]

+ 14 - 0
frameworks/JavaScript/spliffy/spliffy-postgres.dockerfile

@@ -0,0 +1,14 @@
+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
+
+ENV DB_HOST=tfb-database
+ENV DATABASE=postgres
+
+EXPOSE 1420
+
+CMD ["node", "/app/start.js"]

+ 6 - 5
frameworks/JavaScript/spliffy/www/cached-worlds.rt.js

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

+ 2 - 1
frameworks/JavaScript/spliffy/www/db.rt.js

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

+ 23 - 20
frameworks/JavaScript/spliffy/www/fortunes.rt.js

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

+ 2 - 2
frameworks/JavaScript/spliffy/www/queries.rt.js

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

+ 6 - 3
frameworks/JavaScript/spliffy/www/updates.rt.js

@@ -1,13 +1,16 @@
 const db = require( '../db' )
 const db = require( '../db' )
-const { parseCount } = require( '../fn' )
+const { randomId, randomUniqueIds, parseCount } = require( "../fn" );
+
+randomNumber = process.env.DATABASE === 'mongodb' ? 'randomNumber' : 'randomnumber'
+
 module.exports = {
 module.exports = {
     GET: async ( { url: { query: { queries } } } ) => await db.bulkUpdateWorld(
     GET: async ( { url: { query: { queries } } } ) => await db.bulkUpdateWorld(
         await Promise.all(
         await Promise.all(
-            db.randomUniqueIds( parseCount( queries ) )
+            randomUniqueIds( parseCount( queries ) )
                 .map( id =>
                 .map( id =>
                     db.worldById( id )
                     db.worldById( id )
                         .then( world => {
                         .then( world => {
-                            world.randomnumber = db.randomId()
+                            world[randomNumber] = randomId()
                             return world
                             return world
                         } )
                         } )
                 )
                 )