-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPooledDataSource.js
More file actions
111 lines (100 loc) · 3.13 KB
/
PooledDataSource.js
File metadata and controls
111 lines (100 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
* PooledDataSource — DataSource with connection pooling.
*
* Wraps any JSDBC DataSource and adds connection pooling via a pluggable
* ConnectionPool implementation. When getConnection() is called, the pool
* provides a proxy connection whose close() returns it to the pool instead
* of closing the underlying connection.
*
* Three pool options:
* 1. Built-in SimpleConnectionPool (default, no external deps)
* 2. TarnPoolAdapter (pass tarn.js Pool class)
* 3. GenericPoolAdapter (pass generic-pool module)
* 4. Any custom ConnectionPool implementation
*
* Usage:
* const ds = new PooledDataSource({
* url: 'jsdbc:pg://localhost:5432/mydb',
* username: 'user',
* password: 'pass',
* pool: { min: 0, max: 10 },
* });
* const conn = await ds.getConnection();
* // ... use conn ...
* await conn.close(); // returns to pool, doesn't close
* await ds.destroy(); // closes all connections
*/
import DataSource from './DataSource.js';
import SimpleConnectionPool from './SimpleConnectionPool.js';
export default class PooledDataSource extends DataSource {
/**
* @param {Object} config
* @param {string} config.url
* @param {string} [config.username]
* @param {string} [config.password]
* @param {Object} [config.pool] — pool options (min, max, timeouts)
* @param {ConnectionPool} [config.connectionPool] — custom pool instance (overrides pool options)
*/
constructor(config = {}) {
super(config);
this._poolConfig = config.pool || {};
if (config.connectionPool) {
// User-provided pool (tarn adapter, generic-pool adapter, custom)
this._pool = config.connectionPool;
} else {
// Built-in pool
this._pool = new SimpleConnectionPool(
{
create: () => super.getConnection(),
destroy: (conn) => conn.close(),
validate: (conn) => !conn.isClosed(),
},
this._poolConfig,
);
}
}
/**
* Get a pooled connection. The returned connection's close() method
* returns it to the pool instead of closing the underlying connection.
* @returns {Promise<Connection>}
*/
async getConnection() {
const conn = await this._pool.acquire();
return this._wrapConnection(conn);
}
/**
* Destroy the pool — close all connections.
* @returns {Promise<void>}
*/
async destroy() {
await this._pool.destroy();
}
/** @returns {ConnectionPool} The underlying pool. */
getPool() {
return this._pool;
}
/**
* Wrap a connection so close() returns it to the pool.
* @private
*/
_wrapConnection(conn) {
const pool = this._pool;
const realClose = conn.close.bind(conn);
const realIsClosed = conn.isClosed.bind(conn);
// Create a proxy that intercepts close()
const proxy = Object.create(conn);
proxy._released = false;
proxy.close = async () => {
if (!proxy._released) {
proxy._released = true;
await pool.release(conn);
}
};
proxy.isClosed = () => {
return proxy._released || realIsClosed();
};
// Expose the real close for pool destroy
proxy._realClose = realClose;
return proxy;
}
}