mirror of
https://github.com/nodejs/node.git
synced 2026-10-14 10:26:26 -04:00
sqlite: change approach to fix segfault SQLTagStore
PR-URL: https://github.com/nodejs/node/pull/60462 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day> Reviewed-By: Zeyu "Alex" Yang <himself65@outlook.com>
This commit is contained in:
1 parent
f200685d99
commit
f6464c57f6
3 files changed
+42
-3
No files matched your search
+3
-3
@@ -2819,6 +2819,7 @@ BaseObjectPtr<SQLTagStore> SQLTagStore::Create(
|
||||
.ToLocal(&obj)) {
|
||||
return nullptr;
|
||||
}
|
||||
obj->SetInternalField(kDatabaseObject, database->object());
|
||||
return MakeBaseObject<SQLTagStore>(env, obj, std::move(database), capacity);
|
||||
}
|
||||
|
||||
@@ -2829,9 +2830,8 @@ void SQLTagStore::CapacityGetter(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
void SQLTagStore::DatabaseGetter(const FunctionCallbackInfo<Value>& args) {
|
||||
SQLTagStore* store;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&store, args.This());
|
||||
args.GetReturnValue().Set(store->database_->object());
|
||||
args.GetReturnValue().Set(
|
||||
args.This()->GetInternalField(kDatabaseObject).As<Value>());
|
||||
}
|
||||
|
||||
void SQLTagStore::SizeGetter(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
@@ -305,6 +305,11 @@ class Session : public BaseObject {
|
||||
|
||||
class SQLTagStore : public BaseObject {
|
||||
public:
|
||||
enum InternalFields {
|
||||
kDatabaseObject = BaseObject::kInternalFieldCount,
|
||||
kInternalFieldCount
|
||||
};
|
||||
|
||||
SQLTagStore(Environment* env,
|
||||
v8::Local<v8::Object> object,
|
||||
BaseObjectWeakPtr<DatabaseSync> database,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
'use strict';
|
||||
// Flags: --expose-gc
|
||||
|
||||
const { skipIfSQLiteMissing } = require('../common');
|
||||
skipIfSQLiteMissing();
|
||||
|
||||
@@ -135,3 +137,35 @@ test('sql error messages are descriptive', () => {
|
||||
message: /no such table/i,
|
||||
});
|
||||
});
|
||||
|
||||
test('a tag store keeps the database alive by itself', () => {
|
||||
const sql = new DatabaseSync(':memory:').createTagStore();
|
||||
|
||||
sql.db.exec('CREATE TABLE test (data INTEGER)');
|
||||
|
||||
global.gc();
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
sql.run`INSERT INTO test (data) VALUES (1)`;
|
||||
});
|
||||
|
||||
test('tag store prevents circular reference leaks', async () => {
|
||||
const { gcUntil } = require('../common/gc');
|
||||
|
||||
const before = process.memoryUsage().heapUsed;
|
||||
|
||||
// Create many SQLTagStore + DatabaseSync pairs with circular references
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const sql = new DatabaseSync(':memory:').createTagStore();
|
||||
sql.db.exec('CREATE TABLE test (data INTEGER)');
|
||||
// eslint-disable-next-line no-void
|
||||
sql.db.setAuthorizer(() => void sql.db);
|
||||
}
|
||||
|
||||
// GC until memory stabilizes or give up after 20 attempts
|
||||
await gcUntil('tag store leak check', () => {
|
||||
const after = process.memoryUsage().heapUsed;
|
||||
// Memory should not grow significantly (allow 50% margin for noise)
|
||||
return after < before * 1.5;
|
||||
}, 20);
|
||||
});
|
||||
Reference in new issue
Block a user