Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(swing-store): Delete transcript spans in stopUsingTranscript as in rollover #10060

Merged
merged 8 commits into from
Sep 11, 2024
26 changes: 26 additions & 0 deletions packages/internal/tools/ava-assertions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Assert that the contents of `array` are
* [like]{@link https://github.com/avajs/ava/blob/main/docs/03-assertions.md#likeactual-selector-message}
* those of `expected`, including having matching lengths, with pretty diffs in
* case of mismatch.
*
* @param {import('ava').ExecutionContext} t
* @param {unknown[]} array
* @param {unknown[]} expected
* @param {string} [message]
*/
export const arrayIsLike = (t, array, expected, message) => {
const actualLength = array.length;
const expectedLength = expected.length;
const actualExcess = actualLength - expectedLength;
const comparable =
actualExcess > 0
? [...expected, ...Array.from({ length: actualExcess })]
: expected;
t.like(array, comparable, message);

if (actualLength === expectedLength) return;

const extended = [...array, ...Array.from({ length: -actualExcess })];
t.deepEqual(extended, array, message);
};
93 changes: 54 additions & 39 deletions packages/swing-store/src/transcriptStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,12 @@ export function makeTranscriptStore(
*/
function initTranscript(vatID) {
ensureTxn();
const initialIncarnation = 0;
sqlWriteSpan.run(vatID, 0, 0, initialHash, 1, initialIncarnation);
const newRec = spanRec(vatID, 0, 0, initialHash, true, 0);
noteExport(spanMetadataKey(newRec), JSON.stringify(newRec));
const pos = 0;
const isCurrent = 1;
const incarnation = 0;
sqlWriteSpan.run(vatID, pos, pos, initialHash, isCurrent, incarnation);
const rec = spanRec(vatID, pos, pos, initialHash, isCurrent, incarnation);
noteExport(spanMetadataKey(rec), JSON.stringify(rec));
}

const sqlGetCurrentSpanBounds = db.prepare(`
Expand Down Expand Up @@ -255,32 +257,27 @@ export function makeTranscriptStore(
WHERE vatID = ? AND position >= ? AND position < ?
`);

function doSpanRollover(vatID, isNewIncarnation) {
/**
* Finalize a span, setting isCurrent to null, marking the resulting record
* for export, and effecting disk archival and database cleanup as
* configured.
* Note that creation of a new DB row and removal/replacement of the
* transcript.${vatID}.current export record are responsibility of the caller.
*
* @param {string} vatID
* @param {ReturnType<getCurrentSpanBounds>} bounds
*/
function closeSpan(vatID, bounds) {
ensureTxn();
const { hash, startPos, endPos, incarnation } = getCurrentSpanBounds(vatID);
const { startPos, endPos, hash, incarnation } = bounds;
const rec = spanRec(vatID, startPos, endPos, hash, false, incarnation);

// add a new record for the now-old span
// add a new export record for the now-old span
noteExport(spanMetadataKey(rec), JSON.stringify(rec));

// and change its DB row to isCurrent=0
// and change its DB row to isCurrent=null
sqlEndCurrentSpan.run(vatID);

// create a new (empty) row, with isCurrent=1
const incarnationToUse = isNewIncarnation ? incarnation + 1 : incarnation;
sqlWriteSpan.run(vatID, endPos, endPos, initialHash, 1, incarnationToUse);

// overwrite the transcript.${vatID}.current record with new span
const newRec = spanRec(
vatID,
endPos,
endPos,
initialHash,
true,
incarnationToUse,
);
noteExport(spanMetadataKey(newRec), JSON.stringify(newRec));

if (!keepTranscripts) {
// Delete items of the previously-current span.
// There may still be items associated with even older spans, but we leave
Expand All @@ -290,7 +287,32 @@ export function makeTranscriptStore(
// that doesn't include them.
sqlDeleteOldItems.run(vatID, startPos, endPos);
}
return incarnationToUse;
}

function doSpanRollover(vatID, isNewIncarnation) {
ensureTxn();
const bounds = getCurrentSpanBounds(vatID);
const { endPos, incarnation } = bounds;

// deal with the now-old span
closeSpan(vatID, bounds);

// create a new (empty) DB row, with isCurrent=1
const newSpanIncarnation = isNewIncarnation ? incarnation + 1 : incarnation;
sqlWriteSpan.run(vatID, endPos, endPos, initialHash, 1, newSpanIncarnation);

// overwrite the transcript.${vatID}.current record with new span
const rec = spanRec(
vatID,
endPos,
endPos,
initialHash,
true,
newSpanIncarnation,
);
noteExport(spanMetadataKey(rec), JSON.stringify(rec));

return newSpanIncarnation;
}

/**
Expand Down Expand Up @@ -360,28 +382,21 @@ export function makeTranscriptStore(
`);

/**
* Prepare for vat deletion by marking the isCurrent span as not
* current. Idempotent.
* Prepare for vat deletion by marking the isCurrent=1 span as not current.
* Idempotent.
*
* @param {string} vatID The vat being terminated/deleted.
*/
function stopUsingTranscript(vatID) {
ensureTxn();
// this transforms the current span into a (short) historical one
// (basically doSpanRollover without adding replacement data)
const bounds = sqlGetCurrentSpanBounds.get(vatID);
if (bounds) {
// add a new record for the now-old span
const { startPos, endPos, hash, incarnation } = bounds;
const rec = spanRec(vatID, startPos, endPos, hash, false, incarnation);
noteExport(spanMetadataKey(rec), JSON.stringify(rec));
if (!bounds) return;

// and change its DB row to isCurrent=0
sqlEndCurrentSpan.run(vatID);
// deal with the now-old span
closeSpan(vatID, bounds);

// remove the transcript.${vatID}.current record
noteExport(spanMetadataKey({ vatID, isCurrent: true }), undefined);
}
// remove the transcript.${vatID}.current record
noteExport(spanMetadataKey({ vatID, isCurrent: true }), undefined);
}

/**
Expand Down Expand Up @@ -542,7 +557,7 @@ export function makeTranscriptStore(
}
}
} else if (artifactMode === 'archival') {
// every span for all vatIDs that have an isCurrent span (to
// every span for all vatIDs that have an isCurrent=1 span (to
// ignore terminated/partially-deleted vats)
const vatIDs = new Set();
for (const { vatID } of sqlGetCurrentSpanMetadata.iterate()) {
Expand Down
Loading
Loading