Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 140 additions & 2 deletions packages/zero-cache/src/services/change-streamer/storer.pg.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ describe('change-streamer/storer', () => {
fatalErrors = new Queue();

return async () => {
await testDBs.drop(db);
void storer?.stop();
await done;
await testDBs.drop(db);
};
});

Expand All @@ -99,7 +99,7 @@ describe('change-streamer/storer', () => {
const msg: Downstream = JSON.parse(json);
msgs.push(msg);
if (msg[0] === 'commit' && msg[2].watermark === untilWatermark) {
break;
return msgs;
}
}
return msgs;
Expand Down Expand Up @@ -129,6 +129,142 @@ describe('change-streamer/storer', () => {
).toEqual([{ownerAddress: 'change-streamer:12345'}]);
});

test('stores large transactions in batched changeLog inserts without reordering', async () => {
storer.store('08', ['begin', messages.begin(), {commitWatermark: '08'}]);
for (let i = 0; i < 250; i++) {
storer.store('08', [
'data',
messages.insert('issues', {id: `batched-${i}`}),
]);
}
storer.store('08', ['commit', messages.commit(), {watermark: '08'}]);

await storer.allProcessed();
await expectConsumed('08');

const rows = await db<{pos: bigint; tag: string}[]>`
SELECT pos, change->>'tag' as tag
FROM "xero_5/cdc"."changeLog"
WHERE watermark = '08'
ORDER BY pos`;
expect(rows).toHaveLength(252);
expect(rows.at(0)).toEqual({pos: 0n, tag: 'begin'});
expect(rows.at(-1)).toEqual({pos: 251n, tag: 'commit'});
expect(rows.map(row => Number(row.pos))).toEqual(
Array.from({length: 252}, (_, i) => i),
);
});

test('rollback only discards the current transaction from a group', async () => {
storer.store('08', ['begin', messages.begin(), {commitWatermark: '08'}]);
storer.store('08', ['commit', messages.commit(), {watermark: '08'}]);

storer.store('09', ['begin', messages.begin(), {commitWatermark: '09'}]);
storer.store('09', [
'data',
messages.insert('issues', {id: 'rolled-back'}),
]);
storer.store('09', ['rollback', messages.rollback()]);

await storer.allProcessed();
await expectConsumed('08');

expect(
await db`
SELECT watermark, pos, change->>'tag' as tag
FROM "xero_5/cdc"."changeLog"
WHERE watermark >= '08'
ORDER BY watermark, pos`,
).toEqual([
{watermark: '08', pos: 0n, tag: 'begin'},
{watermark: '08', pos: 1n, tag: 'commit'},
]);
expect(
await db`SELECT "lastWatermark" FROM "xero_5/cdc"."replicationState"`,
).toEqual([{lastWatermark: '08'}]);
});

test('status during an open transaction waits for grouped commit', async () => {
storer.store('07', ['begin', messages.begin(), {commitWatermark: '08'}]);
storer.store('07', [
'data',
messages.insert('issues', {id: 'mid-status'}),
]);
storer.status(['status', {ack: true}, {watermark: '07'}]);
storer.store('08', ['commit', messages.commit(), {watermark: '08'}]);

await storer.allProcessed();
await expectConsumed('07', '08');

expect(
await db<{lastWatermark: string}[]>`
SELECT "lastWatermark" FROM "xero_5/cdc"."replicationState"`,
).toEqual([{lastWatermark: '08'}]);
expect(
await db`
SELECT watermark, pos, precommit, change->>'tag' as tag
FROM "xero_5/cdc"."changeLog"
WHERE watermark >= '07'
ORDER BY watermark, pos`,
).toEqual([
{watermark: '07', pos: 0n, precommit: null, tag: 'begin'},
{watermark: '07', pos: 1n, precommit: null, tag: 'insert'},
{watermark: '08', pos: 2n, precommit: '07', tag: 'commit'},
]);

const [sub, _0, stream] = createSubscriber('06');
storer.catchup(sub, 'serving');

expect(await drain(stream, '08')).toMatchInlineSnapshot(`
[
[
"status",
{
"tag": "status",
},
],
[
"begin",
{
"tag": "begin",
},
{
"commitWatermark": "07",
},
],
[
"data",
{
"new": {
"id": "mid-status",
},
"relation": {
"name": "issues",
"rowKey": {
"columns": [
"id",
],
"type": "default",
},
"schema": "public",
"tag": "relation",
},
"tag": "insert",
},
],
[
"commit",
{
"tag": "commit",
},
{
"watermark": "08",
},
],
]
`);
});

test('purge', async () => {
expect(await storer.purgeRecordsBefore('02')).toBe(2);
expect(
Expand Down Expand Up @@ -922,6 +1058,7 @@ describe('change-streamer/storer', () => {

// Now send commit.
storer.store('08', ['commit', messages.commit(), {watermark: '08'}]);
await storer.allProcessed();

// Now an ownership change should succeed.
expect(
Expand All @@ -947,6 +1084,7 @@ describe('change-streamer/storer', () => {
storer.store('0a', ['data', messages.insert('issues', {id: 'bar'})]);
storer.store('0a', ['commit', messages.commit(), {watermark: '0a'}]);

await storer.allProcessed();
await expectConsumed('0a');

expect(
Expand Down
Loading