From e2e513c32249630ac1534db50544683188bbc310 Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Wed, 14 Feb 2024 18:46:55 -0500 Subject: [PATCH] Consolidate block assignment doc reset code. --- lib/IndexAllocationCache.js | 42 ++++++++----- lib/ListManager.js | 116 ++++-------------------------------- 2 files changed, 38 insertions(+), 120 deletions(-) diff --git a/lib/IndexAllocationCache.js b/lib/IndexAllocationCache.js index 2939c2b6..f0d0f102 100644 --- a/lib/IndexAllocationCache.js +++ b/lib/IndexAllocationCache.js @@ -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() { @@ -141,7 +158,7 @@ export class IndexAllocationCache { }, meta: {type} }; - await _resetBlockAssignmentDoc({ + await _initBlockAssignmentDoc({ content: doc.content, slSequence: item.slSequence, statusListConfig }); try { @@ -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; @@ -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; diff --git a/lib/ListManager.js b/lib/ListManager.js index 04acc16b..a9755827 100644 --- a/lib/ListManager.js +++ b/lib/ListManager.js @@ -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, @@ -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; } @@ -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 { @@ -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; @@ -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 }) { @@ -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) {