Compare commits

..

No commits in common. "24887caeb0fbd23a335796edd44cd49f2d86eba4" and "e168e2eb33980c7d099ee51b0fbc91d41793af5a" have entirely different histories.

4 changed files with 8 additions and 88 deletions

View File

@ -346,18 +346,4 @@ export default class GapCache implements GapCacheInterface {
toArray(): GapSetEntry[] { toArray(): GapSetEntry[] {
return this._exposedGaps.toArray(); return this._exposedGaps.toArray();
} }
/**
* Clear all exposed and staging gaps from the cache.
*
* Note: retains invalidating updates from removeOverlappingGaps()
* for correctness of gaps inserted afterwards.
*
* @return {undefined}
*/
clear(): void {
this._stagingUpdates.newGaps = new GapSet(this.maxGapWeight);
this._frozenUpdates.newGaps = new GapSet(this.maxGapWeight);
this._exposedGaps = new GapSet(this.maxGapWeight);
}
} }

View File

@ -73,14 +73,13 @@ type GapCachingInfo = GapCachingInfo_NoGapCache
export const enum GapBuildingState { export const enum GapBuildingState {
Disabled = 0, // no gap cache or no gap building needed (e.g. in V1 versioning format) Disabled = 0, // no gap cache or not allowed to build due to exposure delay timeout
NotBuilding = 1, // not currently building a gap (i.e. not listing within a gap) NotBuilding = 1, // not currently building a gap (i.e. not listing within a gap)
Building = 2, // currently building a gap (i.e. listing within a gap) Building = 2, // currently building a gap (i.e. listing within a gap)
Expired = 3, // not allowed to build due to exposure delay timeout
}; };
type GapBuildingInfo_NothingToBuild = { type GapBuildingInfo_Disabled = {
state: GapBuildingState.Disabled | GapBuildingState.Expired; state: GapBuildingState.Disabled;
}; };
type GapBuildingParams = { type GapBuildingParams = {
@ -119,7 +118,7 @@ type GapBuildingInfo_Building = {
gapWeight: number; gapWeight: number;
}; };
type GapBuildingInfo = GapBuildingInfo_NothingToBuild type GapBuildingInfo = GapBuildingInfo_Disabled
| GapBuildingInfo_NotBuilding | GapBuildingInfo_NotBuilding
| GapBuildingInfo_Building; | GapBuildingInfo_Building;
@ -215,8 +214,6 @@ export class DelimiterMaster extends Delimiter {
switch (this._gapBuilding.state) { switch (this._gapBuilding.state) {
case GapBuildingState.Disabled: case GapBuildingState.Disabled:
return null; return null;
case GapBuildingState.Expired:
return 0;
case GapBuildingState.NotBuilding: case GapBuildingState.NotBuilding:
gapBuilding = <GapBuildingInfo_NotBuilding> this._gapBuilding; gapBuilding = <GapBuildingInfo_NotBuilding> this._gapBuilding;
break; break;
@ -317,7 +314,6 @@ export class DelimiterMaster extends Delimiter {
_checkGapOnMasterDeleteMarker(key: string): FilterReturnValue { _checkGapOnMasterDeleteMarker(key: string): FilterReturnValue {
switch (this._gapBuilding.state) { switch (this._gapBuilding.state) {
case GapBuildingState.Disabled: case GapBuildingState.Disabled:
case GapBuildingState.Expired:
break; break;
case GapBuildingState.NotBuilding: case GapBuildingState.NotBuilding:
this._createBuildingGap(key, 1); this._createBuildingGap(key, 1);
@ -498,23 +494,16 @@ export class DelimiterMaster extends Delimiter {
return params; return params;
} }
/** _saveBuildingGap(): void {
* Save the gap being built if allowed (i.e. still within the
* allocated exposure time window).
*
* @return {boolean} - true if the gap was saved, false if we are
* outside the allocated exposure time window.
*/
_saveBuildingGap(): boolean {
const { gapCache, params, gap, gapWeight } = const { gapCache, params, gap, gapWeight } =
<GapBuildingInfo_Building> this._gapBuilding; <GapBuildingInfo_Building> this._gapBuilding;
const totalElapsed = Date.now() - params.initTimestamp; const totalElapsed = Date.now() - params.initTimestamp;
if (totalElapsed >= gapCache.exposureDelayMs) { if (totalElapsed >= gapCache.exposureDelayMs) {
this._gapBuilding = { this._gapBuilding = {
state: GapBuildingState.Expired, state: GapBuildingState.Disabled,
}; };
this._refreshedBuildingParams = null; this._refreshedBuildingParams = null;
return false; return;
} }
const { firstKey, lastKey, weight } = gap; const { firstKey, lastKey, weight } = gap;
gapCache.setGap(firstKey, lastKey, weight); gapCache.setGap(firstKey, lastKey, weight);
@ -529,7 +518,6 @@ export class DelimiterMaster extends Delimiter {
}, },
gapWeight, gapWeight,
}; };
return true;
} }
/** /**
@ -581,10 +569,7 @@ export class DelimiterMaster extends Delimiter {
// only set gaps that are significant enough in weight and // only set gaps that are significant enough in weight and
// with a non-empty extension // with a non-empty extension
if (gapWeight >= params.minGapWeight && gap.weight > 0) { if (gapWeight >= params.minGapWeight && gap.weight > 0) {
// we're done if we were not allowed to save the gap this._saveBuildingGap();
if (!this._saveBuildingGap()) {
return;
}
// params may have been refreshed, reload them // params may have been refreshed, reload them
gapBuilding = <GapBuildingInfo_Building> this._gapBuilding; gapBuilding = <GapBuildingInfo_Building> this._gapBuilding;
params = gapBuilding.params; params = gapBuilding.params;

View File

@ -50,52 +50,6 @@ describe('GapCache', () => {
}); });
}); });
describe('clear()', () => {
it('should clear all exposed gaps', async () => {
gapCache.setGap('bar', 'baz', 10);
gapCache.setGap('qux', 'quz', 20);
await new Promise(resolve => setTimeout(resolve, 300));
expect(await gapCache.lookupGap('ape', 'zoo')).toEqual(
{ firstKey: 'bar', lastKey: 'baz', weight: 10 }
);
gapCache.clear();
expect(await gapCache.lookupGap('ape', 'zoo')).toBeNull();
});
it('should clear all staging gaps', async () => {
gapCache.setGap('bar', 'baz', 10);
gapCache.setGap('qux', 'quz', 20);
gapCache.clear();
await new Promise(resolve => setTimeout(resolve, 300));
expect(await gapCache.lookupGap('ape', 'zoo')).toBeNull();
});
it('should keep existing invalidating updates against the next new gaps', async () => {
// invalidate future gaps containing 'dog'
expect(gapCache.removeOverlappingGaps(['dog'])).toEqual(0);
// then, clear the cache
gapCache.clear();
// wait for 50ms (half of exposure delay of 100ms) before
// setting a new gap overlapping with 'dog'
await new Promise(resolve => setTimeout(resolve, 50));
gapCache.setGap('cat', 'fox', 10);
// also set a non-overlapping gap to make sure it is not invalidated
gapCache.setGap('goat', 'hog', 20);
// wait an extra 250ms to ensure all valid gaps have been exposed
await new Promise(resolve => setTimeout(resolve, 250));
// the next gap is indeed 'goat'... because 'cat'... should have been invalidated
expect(await gapCache.lookupGap('bat', 'zoo')).toEqual(
{ firstKey: 'goat', lastKey: 'hog', weight: 20 });
});
});
it('should expose gaps after at least exposureDelayMs milliseconds', async () => { it('should expose gaps after at least exposureDelayMs milliseconds', async () => {
gapCache.setGap('bar', 'baz', 10); gapCache.setGap('bar', 'baz', 10);
expect(await gapCache.lookupGap('ape', 'cat')).toBeNull(); expect(await gapCache.lookupGap('ape', 'cat')).toBeNull();

View File

@ -1186,11 +1186,6 @@ describe('DelimiterMaster listing algorithm: gap caching and lookup', () => {
resumeFromState = filterEntries(listing, 'Ddv Ddv Ddv Vvv', 'ass ass ass ass', resumeFromState = filterEntries(listing, 'Ddv Ddv Ddv Vvv', 'ass ass ass ass',
resumeFromState); resumeFromState);
expect(gapCache.toArray()).toEqual(gapsArray); expect(gapCache.toArray()).toEqual(gapsArray);
// gap building should be in expired state
expect(listing._gapBuilding.state).toEqual(GapBuildingState.Expired);
// remaining validity period should still be 0 because gap building has expired
validityPeriod = listing.getGapBuildingValidityPeriodMs();
expect(validityPeriod).toEqual(0);
// we should still be able to skip over the existing cached gaps // we should still be able to skip over the existing cached gaps
expect(listing._gapCaching.state).toEqual(GapCachingState.GapLookupInProgress); expect(listing._gapCaching.state).toEqual(GapCachingState.GapLookupInProgress);