This commit is contained in:
Nikita Sivukhin
2025-10-29 11:50:31 +04:00
parent 4c98861590
commit b01cec2ba4
4 changed files with 795 additions and 628 deletions

View File

@@ -13,6 +13,63 @@ function cleanup(path) {
try { unlinkSync(`${path}-wal-revert`) } catch (e) { }
}
test('concurrent-actions-consistency', async () => {
{
const db = await connect({
path: ':memory:',
url: process.env.VITE_TURSO_DB_URL,
longPollTimeoutMs: 100,
// tracing: 'trace',
});
await db.exec("CREATE TABLE IF NOT EXISTS rows(key TEXT PRIMARY KEY, value INTEGER)");
await db.exec("DELETE FROM rows");
await db.exec("INSERT INTO rows VALUES ('key', 0)");
await db.push();
await db.close();
}
const db1 = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL });
console.info('run_info', await db1.prepare("SELECT * FROM sqlite_master").all());
await db1.exec("PRAGMA busy_timeout=100");
const pull = async function (iterations: number) {
for (let i = 0; i < iterations; i++) {
console.info('pull', i);
try { await db1.pull(); }
catch (e) { console.error('pull', e); }
await new Promise(resolve => setTimeout(resolve, 0));
}
}
const push = async function (iterations: number) {
for (let i = 0; i < iterations; i++) {
await new Promise(resolve => setTimeout(resolve, (Math.random() + 1)));
// console.info('push', i);
try {
if ((await db1.stats()).operations > 0) {
const start = performance.now();
await db1.push();
console.info('push', performance.now() - start);
}
}
catch (e) { console.error('push', e); }
}
}
const run = async function (iterations: number) {
let rows = 0;
for (let i = 0; i < iterations; i++) {
// console.info('run', i, rows);
// console.info('run_info', 'update', 'start');
await db1.prepare("UPDATE rows SET value = value + 1 WHERE key = ?").run('key');
// console.info('run_info', 'update', 'end');
rows += 1;
// console.info('run_info', 'select', 'start');
const { cnt } = await db1.prepare("SELECT value as cnt FROM rows WHERE key = ?").get(['key']);
// console.info('run_info', 'select', 'end', cnt, '(', rows, ')');
expect(cnt).toBe(rows);
await new Promise(resolve => setTimeout(resolve, 10 * (Math.random() + 1)));
}
}
await Promise.all([pull(20), push(20), run(200)]);
})
test('simple-db', async () => {
const db = new Database({ path: ':memory:' });
expect(await db.prepare("SELECT 1 as x").all()).toEqual([{ x: 1 }])
@@ -393,13 +450,14 @@ test('concurrent-updates', async () => {
await db.push();
await db.close();
}
const db1 = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL });
const db1 = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL, tracing: 'info' });
async function pull(db) {
try {
await db.pull();
} catch (e) {
// ignore
console.error('pull error', e);
} finally {
console.error('pull ok');
setTimeout(async () => await pull(db), 0);
}
}
@@ -407,8 +465,9 @@ test('concurrent-updates', async () => {
try {
await db.push();
} catch (e) {
// ignore
console.error('push error', e);
} finally {
console.error('push ok');
setTimeout(async () => await push(db), 0);
}
}
@@ -416,6 +475,7 @@ test('concurrent-updates', async () => {
setTimeout(async () => await push(db1), 0)
for (let i = 0; i < 1000; i++) {
try {
console.info('changes ' + JSON.stringify(await db1.prepare("SELECT change_id FROM turso_cdc").all()));
await Promise.all([
db1.exec(`INSERT INTO q VALUES ('1', 0) ON CONFLICT DO UPDATE SET y = ${i + 1}`),
db1.exec(`INSERT INTO q VALUES ('2', 0) ON CONFLICT DO UPDATE SET y = ${i + 1}`)
@@ -483,6 +543,59 @@ test('pull-push-concurrent', async () => {
console.info(await db.stats());
})
test('checkpoint-and-actions', async () => {
{
const db = await connect({
path: ':memory:',
url: process.env.VITE_TURSO_DB_URL,
longPollTimeoutMs: 100,
tracing: 'info'
});
await db.exec("CREATE TABLE IF NOT EXISTS rows(key TEXT PRIMARY KEY, value INTEGER)");
await db.exec("DELETE FROM rows");
await db.exec("INSERT INTO rows VALUES ('key', 0)");
await db.push();
await db.close();
}
const db1 = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL });
await db1.exec("PRAGMA busy_timeout=100");
const pull = async function (iterations: number) {
for (let i = 0; i < iterations; i++) {
try {
await db1.pull();
}
catch (e) { console.error('pull', e); }
await new Promise(resolve => setTimeout(resolve, 0));
}
}
const push = async function (iterations: number) {
for (let i = 0; i < iterations; i++) {
await new Promise(resolve => setTimeout(resolve, 5));
try {
if ((await db1.stats()).operations > 0) {
const start = performance.now();
await db1.push();
console.info('push', performance.now() - start);
}
}
catch (e) { console.error('push', e); }
}
}
let rows = 0;
const run = async function (iterations: number) {
for (let i = 0; i < iterations; i++) {
await db1.prepare("UPDATE rows SET value = value + 1 WHERE key = ?").run('key');
rows += 1;
const { cnt } = await db1.prepare("SELECT value as cnt FROM rows WHERE key = ?").get(['key']);
console.info('CHECK', cnt, rows);
expect(cnt).toBe(rows);
await new Promise(resolve => setTimeout(resolve, 10 * (1 + Math.random())));
}
}
// await run(100);
await Promise.all([pull(40), push(40), run(100)]);
})
test('transform', async () => {
{
const db = await connect({