Created
September 28, 2019 23:20
-
-
Save isaacs/48776f89d03fa4577485b6b6c530f2f5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const {executionAsyncId, AsyncResource} = require('async_hooks') | |
const _handle = Symbol('_handle') | |
class ConnectionWrap extends AsyncResource { | |
constructor (connection) { | |
super('connection.' + connection.id) | |
this[_handle] = connection | |
// Polyfill for Node.js 8 and before | |
/* istanbul ignore next */ | |
if (!this.runInAsyncScope) | |
this.runInAsyncScope = (fn, thisArg, ...args) => { | |
this.emitBefore() | |
try { | |
return Reflect.apply(fn, thisArg, args) | |
} finally { | |
this.emitAfter() | |
} | |
} | |
} | |
runQuery (query, cb) { | |
this.runInAsyncScope(() => this[_handle].runQuery(query, cb)) | |
} | |
close (cb) { | |
this.runInAsyncScope(() => this[_handle].pool.close(this[_handle], cb)) | |
} | |
} | |
class Connection { | |
constructor (id, pool) { | |
this.id = id | |
this.consumers = 0 | |
this.pool = pool | |
} | |
close (cb) { | |
console.error('%j: closing in eid %j', this.id, executionAsyncId()) | |
setImmediate(cb) | |
} | |
runQuery (query, cb) { | |
console.error('%j: run %j in eid %j', this.id, query, executionAsyncId()) | |
setImmediate(cb) | |
} | |
} | |
class ConnectionPool { | |
constructor () { | |
this.maxConnections = 5 | |
this.pool = new Map() | |
} | |
close (connection, cb) { | |
connection.consumers -- | |
if (connection.consumers === 0 && this.pool.size < this.maxConnections) { | |
this.pool.delete(connection.id) | |
connection.close(cb) | |
} else | |
setImmediate(cb) | |
} | |
createConnection (id, cb) { | |
const conn = this.pool.get(id) || new Connection(id, this) | |
this.pool.set(id, conn) | |
conn.consumers ++ | |
const wrap = new ConnectionWrap(conn) | |
new ConnectionWrap(conn).runInAsyncScope(() => cb(wrap)) | |
} | |
} | |
// consuming code | |
const pool = new ConnectionPool() | |
const fooConns = [] | |
const foo = () => { | |
setTimeout(() => { | |
console.error('EID=%j', executionAsyncId()) | |
pool.createConnection('foo', conn => { | |
fooConns.push(conn) | |
console.error('created foo conn EID=%j', executionAsyncId()) | |
conn.runQuery('q=1', () => { | |
console.error('ran foo conn query EID=%j', executionAsyncId()) | |
conn.close(() => { | |
console.error('closed foo conn EID=%j', executionAsyncId()) | |
}) | |
}) | |
}) | |
}) | |
} | |
const barConns = [] | |
const bar = () => { | |
setTimeout(() => { | |
console.error('EID=%j', executionAsyncId()) | |
pool.createConnection('bar', conn => { | |
barConns.push(conn) | |
console.error('created bar conn EID=%j', executionAsyncId()) | |
conn.runQuery('q=1', () => { | |
console.error('ran bar conn query EID=%j', executionAsyncId()) | |
conn.close(() => { | |
console.error('closed bar conn EID=%j', executionAsyncId()) | |
}) | |
}) | |
}) | |
}) | |
} | |
foo() | |
foo() | |
bar() | |
bar() | |
process.on('exit', () => { | |
const assert = require('assert') | |
assert(fooConns[0] !== fooConns[1]) | |
assert(fooConns[0][_handle] === fooConns[1][_handle]) | |
assert(barConns[0] !== barConns[1]) | |
assert(barConns[0][_handle] === barConns[1][_handle]) | |
console.error('continuations kept separate, actual connections shared') | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment