Skip to content

Commit 9ad374f

Browse files
committed
fix: use Barrier to ensure deterministic concurrent conflict tests
1 parent adc11fe commit 9ad374f

File tree

4 files changed

+28
-11
lines changed

4 files changed

+28
-11
lines changed

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
},
2929
"dependencies": {
3030
"@matrixai/async-init": "^1.8.1",
31-
"@matrixai/async-locks": "^3.0.0",
31+
"@matrixai/async-locks": "^3.1.1",
3232
"@matrixai/errors": "^1.1.2",
3333
"@matrixai/logger": "^2.3.0",
3434
"@matrixai/resources": "^1.1.3",

tests/DBTransaction.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import path from 'path';
44
import fs from 'fs';
55
import Logger, { LogLevel, StreamHandler } from '@matrixai/logger';
66
import { withF } from '@matrixai/resources';
7-
import { errors as locksErrors } from '@matrixai/async-locks';
7+
import { Barrier, errors as locksErrors } from '@matrixai/async-locks';
88
import DB from '@/DB';
99
import DBTransaction from '@/DBTransaction';
1010
import * as errors from '@/errors';
@@ -260,6 +260,8 @@ describe(DBTransaction.name, () => {
260260
expect(await db.get('hello')).toBeUndefined();
261261
});
262262
test('getForUpdate addresses write-skew by promoting gets into same-value puts', async () => {
263+
// Ensure deterministic concurrency
264+
const barrier = await Barrier.createBarrier(2);
263265
// Snapshot isolation allows write skew anomalies to occur
264266
// A write skew means that 2 transactions concurrently read from overlapping keys
265267
// then make disjoint updates to the keys, that breaks a consistency constraint on those keys
@@ -272,13 +274,15 @@ describe(DBTransaction.name, () => {
272274
const t1 = withF([db.transaction()], async ([tran]) => {
273275
let balance1 = parseInt((await tran.getForUpdate('balance1'))!);
274276
const balance2 = parseInt((await tran.getForUpdate('balance2'))!);
277+
await barrier.wait();
275278
balance1 -= 100;
276279
expect(balance1 + balance2).toBeGreaterThanOrEqual(0);
277280
await tran.put('balance1', balance1.toString());
278281
});
279282
const t2 = withF([db.transaction()], async ([tran]) => {
280283
const balance1 = parseInt((await tran.getForUpdate('balance1'))!);
281284
let balance2 = parseInt((await tran.getForUpdate('balance2'))!);
285+
await barrier.wait();
282286
balance2 -= 100;
283287
expect(balance1 + balance2).toBeGreaterThanOrEqual(0);
284288
await tran.put('balance2', balance2.toString());
@@ -298,16 +302,20 @@ describe(DBTransaction.name, () => {
298302
).toBe(true);
299303
});
300304
test('locking to prevent thrashing for racing counters', async () => {
305+
// Ensure deterministic concurrency
306+
const barrier = await Barrier.createBarrier(2);
301307
await db.put('counter', '0');
302308
let t1 = withF([db.transaction()], async ([tran]) => {
303309
// Can also use `getForUpdate`, but a conflict exists even for `get`
304310
let counter = parseInt((await tran.get('counter'))!);
311+
await barrier.wait();
305312
counter++;
306313
await tran.put('counter', counter.toString());
307314
});
308315
let t2 = withF([db.transaction()], async ([tran]) => {
309316
// Can also use `getForUpdate`, but a conflict exists even for `get`
310317
let counter = parseInt((await tran.get('counter'))!);
318+
await barrier.wait();
311319
counter++;
312320
await tran.put('counter', counter.toString());
313321
});

tests/rocksdb/rocksdbP.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { RocksDBDatabase } from '@/native/types';
22
import os from 'os';
33
import path from 'path';
44
import fs from 'fs';
5+
import { Barrier } from '@matrixai/async-locks';
56
import rocksdbP from '@/native/rocksdbP';
67

78
describe('rocksdbP', () => {
@@ -240,6 +241,8 @@ describe('rocksdbP', () => {
240241
);
241242
});
242243
test('transactionGetForUpdate addresses write skew by promoting gets into same-value puts', async () => {
244+
// Ensure deterministic concurrency
245+
const barrier = await Barrier.createBarrier(2);
243246
// Snapshot isolation allows write skew anomalies to occur
244247
// A write skew means that 2 transactions concurrently read from overlapping keys
245248
// then make disjoint updates to the keys, that breaks a consistency constraint on those keys
@@ -257,6 +260,7 @@ describe('rocksdbP', () => {
257260
const balance2 = parseInt(
258261
await rocksdbP.transactionGetForUpdate(tran1, 'balance2', {}),
259262
);
263+
await barrier.wait();
260264
balance1 -= 100;
261265
expect(balance1 + balance2).toBeGreaterThanOrEqual(0);
262266
await rocksdbP.transactionPut(tran1, 'balance1', balance1.toString());
@@ -270,6 +274,7 @@ describe('rocksdbP', () => {
270274
let balance2 = parseInt(
271275
await rocksdbP.transactionGetForUpdate(tran2, 'balance2', {}),
272276
);
277+
await barrier.wait();
273278
balance2 -= 100;
274279
expect(balance1 + balance2).toBeGreaterThanOrEqual(0);
275280
await rocksdbP.transactionPut(tran2, 'balance2', balance2.toString());
@@ -292,6 +297,8 @@ describe('rocksdbP', () => {
292297
).toBe(true);
293298
});
294299
test('transactionMultiGetForUpdate addresses write skew by promoting gets into same-value puts', async () => {
300+
// Ensure deterministic concurrency
301+
const barrier = await Barrier.createBarrier(2);
295302
// Snapshot isolation allows write skew anomalies to occur
296303
// A write skew means that 2 transactions concurrently read from overlapping keys
297304
// then make disjoint updates to the keys, that breaks a consistency constraint on those keys
@@ -321,6 +328,7 @@ describe('rocksdbP', () => {
321328
)
322329
)[0],
323330
);
331+
await barrier.wait();
324332
balance1 -= 100;
325333
expect(balance1 + balance2).toBeGreaterThanOrEqual(0);
326334
await rocksdbP.transactionPut(tran1, 'balance1', balance1.toString());
@@ -346,6 +354,7 @@ describe('rocksdbP', () => {
346354
)
347355
)[0],
348356
);
357+
await barrier.wait();
349358
balance2 -= 100;
350359
expect(balance1 + balance2).toBeGreaterThanOrEqual(0);
351360
await rocksdbP.transactionPut(tran2, 'balance2', balance2.toString());

0 commit comments

Comments
 (0)