Skip to content

Commit

Permalink
Consolidate block assignment doc reset code.
Browse files Browse the repository at this point in the history
  • Loading branch information
dlongley committed Feb 14, 2024
1 parent 6a29e56 commit e2e513c
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 120 deletions.
42 changes: 28 additions & 14 deletions lib/IndexAllocationCache.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,37 @@ export class IndexAllocationCache {
this.edvClient = edvClient;
this.records = [];
this.statusListConfig = statusListConfig;
this._outOfSync = false;
}

// populate this cache from the given items
async populate({items} = {}) {
this._outOfSync = false;
this.records = await Promise.all(items.map(
item => this._readBlockAssignmentDoc({item})));
}

outOfSync() {
// if out-of-sync previously detected, return it
if(this._outOfSync) {
return true;
}
// if any record has a BAD SL sequence number that is greater than the
// LMD item SL sequence number, then the cache is out of sync
const {records} = this;
return records.some(({blockAssignmentDoc: {content}, item}) =>
this._outOfSync = records.some(({blockAssignmentDoc: {content}, item}) =>
content.slSequence > item.slSequence);
return this._outOfSync;
}

async resetBlockAssignmentDoc({record} = {}) {
const {edvClient, statusListConfig} = this;
const {content} = record.blockAssignmentDoc;
await _initBlockAssignmentDoc({
content, slSequence: record.item.slSequence, statusListConfig
});
record.blockAssignmentDoc = await edvClient.update(
{doc: record.blockAssignmentDoc});
}

async selectShard() {
Expand Down Expand Up @@ -141,7 +158,7 @@ export class IndexAllocationCache {
},
meta: {type}
};
await _resetBlockAssignmentDoc({
await _initBlockAssignmentDoc({
content: doc.content, slSequence: item.slSequence, statusListConfig
});
try {
Expand Down Expand Up @@ -209,24 +226,22 @@ export class IndexAllocationCache {
return cacheRecord;
}

const {edvClient, statusListConfig} = this;

try {
// reset block assignment doc
const {content} = cacheRecord.blockAssignmentDoc;
await _resetBlockAssignmentDoc({
content, slSequence: cacheRecord.item.slSequence, statusListConfig
});
cacheRecord.blockAssignmentDoc = await edvClient.update({
doc: cacheRecord.blockAssignmentDoc
});
await this.resetBlockAssignmentDoc({record: cacheRecord});
} catch(e) {
if(e.name !== 'InvalidStateError') {
throw e;
}
cacheRecord.blockAssignmentDoc = await edvClient.get({
// get refreshed record
cacheRecord.blockAssignmentDoc = await this.edvClient.get({
id: cacheRecord.blockAssignmentDoc.id
});
// set out-of-sync flag if sequence does not match
if(cacheRecord.blockAssignmentDoc.content.slSequence !==
cacheRecord.item.slSequence) {
this._outOfSync = true;
}
}

return cacheRecord;
Expand Down Expand Up @@ -269,8 +284,7 @@ async function _chooseRandom({records}) {
}
}

// FIXME: either consolidate or only use in this file not also `ListManager`
async function _resetBlockAssignmentDoc({
async function _initBlockAssignmentDoc({
content, slSequence, statusListConfig
}) {
content.slSequence = slSequence;
Expand Down
116 changes: 10 additions & 106 deletions lib/ListManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ this._readActiveBlockAssignmentDocs(LMD): Reads all active BADs to enable
1. While index allocation active cache is not set:
1.1. Call _tryAddCapacity(LMD, target=1).
1.2. Read all active BADs into an index allocation cache, `cache`.
1.3. If any BAD has an SL sequence number that is ahead of the
1.3. If any BAD has an SL sequence number that does not match the
LMD "active" set item, read the LMD and loop to 1.
1.4. If any BADs have fully assigned blocks of indexes:
1.4.1. Move BAD IDs to the "inactive" set; if all BADs are fully assigned,
Expand Down Expand Up @@ -269,9 +269,10 @@ export class ListManager {
const {blockAssignment} = this.lmDoc.content;
const cache = new IndexAllocationCache({edvClient, statusListConfig});
await cache.populate({items: blockAssignment.active});
// 1.3. If any BAD has an SL sequence number that is ahead of the
// 1.3. If any BAD has an SL sequence number that does not match the
// LMD "active" set item, read the LMD and loop to 1.
if(cache.outOfSync()) {
// FIXME: add a test to ensure this runs
this.lmDoc = await edvClient.get({id: this.lmDoc.id});
continue;
}
Expand Down Expand Up @@ -321,12 +322,6 @@ export class ListManager {
toUpdate.item.statusListCredential = slcIds[0].id;
// use maximum `slSequence` from all fully assigned items, plus 1
toUpdate.item.slSequence = _maxSlSequence(items) + 1;
// reset BAD content automatically so it doesn't need to be
// re-read again and updated in 1.3.2 in the optimistic case
const {content} = toUpdate.blockAssignmentDoc;
await _resetBlockAssignmentDoc({
content, slSequence: toUpdate.item.slSequence, statusListConfig
});
}
// 1.4.2. CW update LMD.
try {
Expand All @@ -341,15 +336,16 @@ export class ListManager {
}
// 1.4.4. CW update active BAD, if one was updated.
if(toUpdate) {
/* Note: If `continue` were just called here instead, it would work
properly, but be slower because the active items all have to be
reread when that is otherwise not required (if the update to
`toUpdate` succeeds w/o conflict). */
/* Note: We reset and update the newly active BAD here so it won't
need to be reread again in `step 1.2` the optimistic case. If
`continue` were just called here instead, it would work properly, but
be slower because the active items all have to be reread when that is
otherwise not required (if the update to `toUpdate` succeeds w/o
conflict). */
// FIXME: determine how to add a test that would skip this
// update / cause a conflict that would trigger looping
try {
toUpdate.blockAssignmentDoc = await edvClient.update(
{doc: toUpdate.blockAssignmentDoc});
await cache.resetBlockAssignmentDoc({record: toUpdate});
} catch(e) {
if(e.name !== 'InvalidStateError') {
throw e;
Expand Down Expand Up @@ -595,54 +591,6 @@ export class ListManager {
}
}

// read BAD contents referenced from LM doc item into a cache record
async _readBlockAssignmentDoc({item}) {
// get `edvClient` directly; do not use cache in `documentStore` to ensure
// latest docs are used
const {documentStore: {edvClient}, statusListConfig} = this;

// executes step 1.2.1 of `_readActiveBlockAssignmentDocs()`
let blockAssignmentDoc;
while(!blockAssignmentDoc) {
try {
blockAssignmentDoc = await edvClient.get({
id: item.blockAssignmentEdvDocId
});
} catch(e) {
if(e.name !== 'NotFoundError') {
throw e;
}

// next, lazily create BAD
const type = 'StatusListBlockAssignmentDocument';
const doc = {
id: item.blockAssignmentEdvDocId,
content: {
id: `urn:uuid:${uuid()}`,
type
},
meta: {type}
};
await _resetBlockAssignmentDoc({
content: doc.content, slSequence: item.slSequence, statusListConfig
});
try {
blockAssignmentDoc = await edvClient.update({doc});
} catch(e) {
if(e.name !== 'DuplicateError') {
throw e;
}
// duplicate, ignore and loop to read doc
}
}
}

// executes step 1.2.2 of `_readActiveBlockAssignmentDocs()`
const cacheRecord = {blockAssignmentDoc, item};
await this._syncCacheRecord(cacheRecord);
return cacheRecord;
}

async _getIndexAssignmentDoc({
blockAssignmentDocId, blockIndex, slSequence
}) {
Expand Down Expand Up @@ -691,50 +639,6 @@ export class ListManager {
unique: true
});
}

// ensure cache record BAD contents are in sync with LM doc item
async _syncCacheRecord(cacheRecord) {
// 1.2.2. If any BAD has an SL sequence number that is behind an LMD item,
// CW update it. If conflict, read the BAD.
if(cacheRecord.blockAssignmentDoc.content.slSequence >=
cacheRecord.item.slSequence) {
return cacheRecord;
}

const {documentStore: {edvClient}, statusListConfig} = this;

try {
// reset block assignment doc
const {content} = cacheRecord.blockAssignmentDoc;
await _resetBlockAssignmentDoc({
content, slSequence: cacheRecord.item.slSequence, statusListConfig
});
cacheRecord.blockAssignmentDoc = await edvClient.update({
doc: cacheRecord.blockAssignmentDoc
});
} catch(e) {
if(e.name !== 'InvalidStateError') {
throw e;
}
cacheRecord.blockAssignmentDoc = await edvClient.get({
id: cacheRecord.blockAssignmentDoc.id
});
}

return cacheRecord;
}
}

async function _resetBlockAssignmentDoc({
content, slSequence, statusListConfig
}) {
content.slSequence = slSequence;
content.blockCount = statusListConfig.options.blockCount;
content.blockSize = statusListConfig.options.blockSize;
// TODO: optimize by using string from config or static var
const bs = new Bitstring({length: content.blockCount});
content.assignedBlocks = await bs.encodeBits();
content.assignedBlockCount = 0;
}

function _maxSlSequence(items) {
Expand Down

0 comments on commit e2e513c

Please sign in to comment.