From ee84e3a6d7be136382aaf5b5c0b7d81979d18e53 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 24 Jul 2015 14:47:16 -0600 Subject: [PATCH] update --- README.md | 129 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 117 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b6f29fc..7c61204 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -DRAFT -===== +SQLite3 Cluster +=============== -This is just hypothetical while I build out the API +Works with node cluster, or completely and unrelated node processes. -SQLite3 Server -============= +**Note**: Most people would probably prefer to just use PostgreSQL rather than +wrap SQLite as a service... but I am not most people. Node.js runs on a single core, which isn't very effective. @@ -15,21 +15,126 @@ This module will either run client-server style in environments that benefit fro (such as the Raspberry Pi 2 with 4 cores), or in-process for environments that don't (such as the Raspberry Pi B and B+). +This also works with **SQLCipher**. + Usage ===== +The default behavior is to try to connect to a master and, if that fails, to become the master. + +However, if you are in fact using the `cluster` rather than spinning up random instances, +you'll probably prefer to use this pattern: + ```js -var sqlite = require('sqlite3-server'); +var cluster = require('cluster'); +var sqlite = require('sqlite3-cluster'); +var numCores = require('os').cpus().length; + var opts = { - key: '1892d335081d8d346e556c9c3c8ff2c3' -, bits: 128 -, filename: path.join('/tmp/authn.sqlcipher') + filename: '/tmp/mydb.sqlcipher' +, sock: '/tmp/mydb.sqlcipher.sock' , verbose: false -, port: 3232 // default random -, forceServer: true // default false + + // a good default to use for instances where you might want + // to cluster or to run standalone, but with the same API +, serve: cluster.isMaster +, connect: cluster.isWorker +, standalone: (1 === numCores) // overrides serve and connect + + // if using SQLCipher, you can supply the key and desired bit-length + // and the appropriate PRAGMA statements will be issued before the database is returned +, key: '00000000000000000000000000000000' +, bits: 128 }; sqlite.create(opts).then(function (db) { - // EXACT same api as db + // same api as new sqlite3.Database(options.filename) + + client.run("SELECT 1", [], function (err) { + if (err) { + console.error('[ERROR]', cluster.isMaster && '0' || cluster.worker.id); + console.error(err); + return; + } + + console.log('[this]', cluster.isMaster && '0' || cluster.worker.id); + console.log(this); + }); }); ``` + +If you wish to always use clustering, even on a single core system, see `test-cluster.js`. + +Likewise, if you wish to use standalone mode in a particular worker process see `test-standalone.js`. + +API +=== + +The API is exactly the same as `node-sqlite`, with these few exceptions: + +1 Database Creation +------------------- + +Instead of this: + +```js +var db = new require('sqlite3').Database(filename); +``` + +You must do this: + +```js +require('sqlite3-cluster').create(filename); +``` + +2 db.escape +----------- + +This is an additional helper function. + +If you need at any time to concatonate strings with user input +(which you should rarely need to do since `db.run(stmt, arr, fn)` +is usually sufficient), you can use the escape function. + +```js +var sqlEscape = require('sqlite3-cluster').escape; +``` + +also + +```js +require('sqlite3-cluster').create(options).then(function (db) { + // obligatory xkcd reference https://xkcd.com/327/ + var userInput = db.escape("Robert'); DROP TABLE Students;"); +}); +``` + +3 serialize / parallelize +------------------------- + +`db.serialize(fn)` and `db.parallelize(fn)` are not supported because it would require +copying a chunk of code from `node-sqlite3` and adapting it. + +It wouldn't be a difficult task, just tedious and generally no longer necessary since +recent versions of node include native `Promise`s. + +Standalone / Master Mode is raw sqlite3 +======================== + +The `master` in the cluster (meaning `opts.serve = true`) will have a direct connection +to the sqlite3 database using `node-sqlite`, directly. + +Likewise, when only one process is being used (`opts.standalone = true`) the listener is +not started and the connection is direct. + +If you take a look at `wrapper.js` you'll see that it simply resolves with an instance of +`node-sqlite3`. + +Security Warning +================ + +Note that any application on the system could connect to the socket. + +In the future I may add a `secret` field in the options object to be +used for authentication across processes. This would not be difficult, +it's just not necessary for my use case at the moment.