mm_sqlite 1.3.9 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/db.js +200 -264
- package/index.js +128 -509
- package/package.json +1 -1
- package/pool.js +241 -0
- package/sql.js +311 -414
- package/test_db/test.db +0 -0
- package/test_db/test_basic.db +0 -0
- package/test_db/test_core_methods.db +0 -0
package/package.json
CHANGED
package/pool.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
const sqlite3 = require('sqlite3').verbose();
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SQLite连接池类
|
|
5
|
+
* @class SQLitePool
|
|
6
|
+
*/
|
|
7
|
+
class SQLitePool {
|
|
8
|
+
/**
|
|
9
|
+
* 构造函数
|
|
10
|
+
* @param {object} config - 连接池配置
|
|
11
|
+
* @param {function} getDbFilename - 获取数据库文件名的函数
|
|
12
|
+
* @param {function} log - 日志函数
|
|
13
|
+
*/
|
|
14
|
+
constructor(config, getDbFilename, log) {
|
|
15
|
+
this.config = config;
|
|
16
|
+
this._getDbFilename = getDbFilename;
|
|
17
|
+
this.log = log;
|
|
18
|
+
|
|
19
|
+
// 连接池状态
|
|
20
|
+
this._pool_connections = [];
|
|
21
|
+
this._pool_available = [];
|
|
22
|
+
this._pool_waiting = [];
|
|
23
|
+
this._pool_reaper = null;
|
|
24
|
+
this._is_initialized = false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 初始化连接池
|
|
29
|
+
* @returns {Promise<void>}
|
|
30
|
+
*/
|
|
31
|
+
async init() {
|
|
32
|
+
if (this._is_initialized) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 创建最小连接数
|
|
37
|
+
for (let i = 0; i < this.config.pool_min; i++) {
|
|
38
|
+
const conn = await this._createConnection();
|
|
39
|
+
this._pool_connections.push(conn);
|
|
40
|
+
this._pool_available.push(conn);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 启动连接回收器
|
|
44
|
+
this._pool_reaper = setInterval(() => {
|
|
45
|
+
this._reapIdleConnections();
|
|
46
|
+
}, this.config.pool_reap_interval);
|
|
47
|
+
|
|
48
|
+
this._is_initialized = true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 获取数据库连接
|
|
53
|
+
* @param {number} timeout - 超时时间(毫秒)
|
|
54
|
+
* @returns {Promise<object>} 数据库连接
|
|
55
|
+
*/
|
|
56
|
+
async getConnection(timeout) {
|
|
57
|
+
const acquire_timeout = timeout || this.config.pool_acquire_timeout || 10000;
|
|
58
|
+
const start_time = Date.now();
|
|
59
|
+
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
let timeout_id = null;
|
|
62
|
+
|
|
63
|
+
const cleanup = () => {
|
|
64
|
+
if (timeout_id) {
|
|
65
|
+
clearTimeout(timeout_id);
|
|
66
|
+
timeout_id = null;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const tryGetConnection = () => {
|
|
71
|
+
// 检查超时
|
|
72
|
+
if (Date.now() - start_time > acquire_timeout) {
|
|
73
|
+
cleanup();
|
|
74
|
+
reject(new Error(`获取连接超时(${acquire_timeout}ms)`));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 检查是否有可用连接
|
|
79
|
+
if (this._pool_available.length > 0) {
|
|
80
|
+
const conn = this._pool_available.shift();
|
|
81
|
+
conn._last_used = Date.now();
|
|
82
|
+
cleanup();
|
|
83
|
+
resolve(conn);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 检查是否可以创建新连接
|
|
88
|
+
if (this._pool_connections.length < this.config.pool_max) {
|
|
89
|
+
this._createConnection()
|
|
90
|
+
.then(conn => {
|
|
91
|
+
this._pool_connections.push(conn);
|
|
92
|
+
conn._last_used = Date.now();
|
|
93
|
+
conn.release = () => this.releaseConn(conn);
|
|
94
|
+
cleanup();
|
|
95
|
+
resolve(conn);
|
|
96
|
+
})
|
|
97
|
+
.catch(err => {
|
|
98
|
+
cleanup();
|
|
99
|
+
reject(err);
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 没有可用连接且无法创建新连接,加入等待队列
|
|
105
|
+
this._pool_waiting.push({
|
|
106
|
+
resolve: (conn) => {
|
|
107
|
+
cleanup();
|
|
108
|
+
resolve(conn);
|
|
109
|
+
},
|
|
110
|
+
reject: (err) => {
|
|
111
|
+
cleanup();
|
|
112
|
+
reject(err);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// 设置重试定时器
|
|
117
|
+
timeout_id = setTimeout(() => {
|
|
118
|
+
tryGetConnection();
|
|
119
|
+
}, 100);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
tryGetConnection();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 释放连接
|
|
128
|
+
* @param {object} conn - 数据库连接
|
|
129
|
+
*/
|
|
130
|
+
releaseConn(conn) {
|
|
131
|
+
if (!conn || !this._pool_connections.includes(conn)) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 标记连接最后使用时间
|
|
136
|
+
conn._last_used = Date.now();
|
|
137
|
+
|
|
138
|
+
// 将连接放回可用连接池
|
|
139
|
+
if (!this._pool_available.includes(conn)) {
|
|
140
|
+
this._pool_available.push(conn);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 如果有等待的连接请求,立即处理
|
|
144
|
+
if (this._pool_waiting.length > 0) {
|
|
145
|
+
const waiting = this._pool_waiting.shift();
|
|
146
|
+
const next_conn = this._pool_available.shift();
|
|
147
|
+
if (next_conn && waiting.resolve) {
|
|
148
|
+
waiting.resolve(next_conn);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 关闭连接池
|
|
155
|
+
*/
|
|
156
|
+
close() {
|
|
157
|
+
if (this._pool_reaper) {
|
|
158
|
+
clearInterval(this._pool_reaper);
|
|
159
|
+
this._pool_reaper = null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 关闭所有连接
|
|
163
|
+
this._pool_connections.forEach(conn => {
|
|
164
|
+
try {
|
|
165
|
+
conn.close((err) => {
|
|
166
|
+
if (err) {
|
|
167
|
+
this.log('error', '关闭连接池连接失败', err);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
} catch (error) {
|
|
171
|
+
this.log('error', '关闭连接池连接异常', error);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
this._pool_connections = [];
|
|
176
|
+
this._pool_available = [];
|
|
177
|
+
this._pool_waiting = [];
|
|
178
|
+
this._is_initialized = false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 获取连接池统计信息
|
|
183
|
+
* @returns {object} 统计信息
|
|
184
|
+
*/
|
|
185
|
+
getStats() {
|
|
186
|
+
return {
|
|
187
|
+
total: this._pool_connections.length,
|
|
188
|
+
available: this._pool_available.length,
|
|
189
|
+
waiting: this._pool_waiting.length,
|
|
190
|
+
is_initialized: this._is_initialized
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 创建SQLite连接
|
|
196
|
+
* @private
|
|
197
|
+
* @returns {Promise<object>} 数据库连接对象
|
|
198
|
+
*/
|
|
199
|
+
_createConnection() {
|
|
200
|
+
return new Promise((resolve, reject) => {
|
|
201
|
+
const db = new sqlite3.Database(this._getDbFilename(), (err) => {
|
|
202
|
+
if (err) {
|
|
203
|
+
reject(err);
|
|
204
|
+
} else {
|
|
205
|
+
db.release = () => this.releaseConn(db);
|
|
206
|
+
resolve(db);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* 回收空闲连接
|
|
214
|
+
* @private
|
|
215
|
+
*/
|
|
216
|
+
_reapIdleConnections() {
|
|
217
|
+
const now = Date.now();
|
|
218
|
+
const idle_timeout = this.config.pool_idle_timeout;
|
|
219
|
+
|
|
220
|
+
for (let i = this._pool_available.length - 1; i >= 0; i--) {
|
|
221
|
+
const conn = this._pool_available[i];
|
|
222
|
+
if (conn._last_used && now - conn._last_used > idle_timeout) {
|
|
223
|
+
// 关闭并移除空闲连接
|
|
224
|
+
conn.close((err) => {
|
|
225
|
+
if (err) {
|
|
226
|
+
this.log('error', '关闭空闲连接失败', err);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// 从连接池中移除
|
|
231
|
+
const conn_idx = this._pool_connections.indexOf(conn);
|
|
232
|
+
if (conn_idx > -1) {
|
|
233
|
+
this._pool_connections.splice(conn_idx, 1);
|
|
234
|
+
}
|
|
235
|
+
this._pool_available.splice(i, 1);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
module.exports = SQLitePool;
|