add assertion on trustedPromise !== null

This commit is contained in:
Michael Peters 2022-10-19 22:02:17 -07:00
parent cdc3307cb2
commit 5df8948886

View File

@ -6,6 +6,7 @@ const LOG = Logger.create(__filename, electronConsole);
import { Changes, WithEquals } from './data-types'; import { Changes, WithEquals } from './data-types';
import * as uuid from 'uuid'; import * as uuid from 'uuid';
import assert from 'assert';
export enum AutoVerifierChangesType { export enum AutoVerifierChangesType {
NONE, // both primaryFunc and trustedFunc returned null NONE, // both primaryFunc and trustedFunc returned null
@ -217,40 +218,23 @@ export class AutoVerifier<T> {
} }
const tryResolveTrustedPromise = async () => { const tryResolveTrustedPromise = async () => {
if (this.trustedPromise) { assert(this.trustedPromise !== null)
// there is a trusted promise that we can check
if (this.trustedStatus === 'fetching') { if (this.trustedStatus === 'fetching') {
// no one has started verifying the trusted yet // no one has started verifying the trusted yet
this.trustedStatus = 'verifying'; this.trustedStatus = 'verifying';
// note: Promises that have already resolved will return the same value when awaited again :) // note: Promises that have already resolved will return the same value when awaited again :)
const origTrustedPromise: Promise<T | null> | null = this.trustedPromise; const origTrustedPromise: Promise<T | null> = this.trustedPromise;
let trustedResult = undefined; let trustedResult = undefined;
try { try {
trustedResult = await origTrustedPromise; trustedResult = await origTrustedPromise;
} catch (e: unknown) { } catch (e: unknown) {
if (this.trustedPromise === origTrustedPromise) { if (this.trustedPromise === origTrustedPromise) {
throw e; throw e;
} else { } else {
console.warn('trusted promise rejected after unverify', e); console.warn('trusted promise rejected after unverify', e);
if (this.trustedPromise === null) {
this.trustedStatus = 'fetching';
this.trustedPromise = this.trustedFunc();
}
await tryResolveTrustedPromise();
return;
}
}
if (this.trustedPromise !== origTrustedPromise) {
// we've been invalidated while we were waiting for the trusted result!
// TODO: This happens when a socket fetch is sent before the socket is connected to.
console.warn(
'RARE ALERT: we got unverified while trying to fetch a trusted promise for verification!',
new Error(),
);
if (this.trustedPromise === null) { if (this.trustedPromise === null) {
this.trustedStatus = 'fetching'; this.trustedStatus = 'fetching';
this.trustedPromise = this.trustedFunc(); this.trustedPromise = this.trustedFunc();
@ -258,87 +242,96 @@ export class AutoVerifier<T> {
await tryResolveTrustedPromise(); await tryResolveTrustedPromise();
return; return;
} }
}
// make sure to verify BEFORE potentially resolving if (this.trustedPromise !== origTrustedPromise) {
// this way the conflicts can be resolved before the result is returned // we've been invalidated while we were waiting for the trusted result!
this.verifyPromise = this.verifyFunc(primaryResult, trustedResult); // TODO: This happens when a socket fetch is sent before the socket is connected to.
const primaryUpToDate = await this.verifyPromise; console.warn(
'RARE ALERT: we got unverified while trying to fetch a trusted promise for verification!',
if (this.trustedPromise === origTrustedPromise) { new Error(),
if (trustedResult !== null && primaryUpToDate) { );
// we got a good trusted result and the primary data has been updated if (this.trustedPromise === null) {
// to reflect the trusted data (or already reflects it). this.trustedStatus = 'fetching';
this.trustedStatus = 'verified'; this.trustedPromise = this.trustedFunc();
} else {
// we have to re-fetch the trusted promise again next fetch
this.trustedStatus = 'none';
}
} else {
// this actually could be quite common
//console.warn('RARE ALERT: we got unverified during verification!');
// ** complexity:
// if primaryUpToDate is false or we got unverified during verification, this path will still return
// the trustedResult that was passed to the verify function
} }
await tryResolveTrustedPromise();
return;
}
if (!resolved) { // make sure to verify BEFORE potentially resolving
// removed 01/09/2021 pretty sure should not be here... && trustedResult // this way the conflicts can be resolved before the result is returned
resolve(trustedResult); this.verifyPromise = this.verifyFunc(primaryResult, trustedResult);
resolved = true; const primaryUpToDate = await this.verifyPromise;
if (this.trustedPromise === origTrustedPromise) {
if (trustedResult !== null && primaryUpToDate) {
// we got a good trusted result and the primary data has been updated
// to reflect the trusted data (or already reflects it).
this.trustedStatus = 'verified';
} else {
// we have to re-fetch the trusted promise again next fetch
this.trustedStatus = 'none';
} }
} else { } else {
// some code is already dealing with (or dealt with) verifying the trusted result // this actually could be quite common
// await the same trusted promise and return its result if we didn't get a result //console.warn('RARE ALERT: we got unverified during verification!');
// from the primary source. // ** complexity:
// if primaryUpToDate is false or we got unverified during verification, this path will still return
// the trustedResult that was passed to the verify function
}
// note: Promises that have already resolved will return the same value when awaited again :) if (!resolved) {
const origTrustedPromise: Promise<T | null> | null = this.trustedPromise; // removed 01/09/2021 pretty sure should not be here... && trustedResult
let trustedResult = undefined; resolve(trustedResult);
try { resolved = true;
trustedResult = await origTrustedPromise; }
} catch (e: unknown) { } else {
if (this.trustedPromise === origTrustedPromise) { // some code is already dealing with (or dealt with) verifying the trusted result
// TODO: decide if can unverify here rather than in the "catch-all" // await the same trusted promise and return its result if we didn't get a result
// maybe this will let us get rid of the catch all? // from the primary source.
throw e;
} else {
console.warn('trusted promise from another path rejected after unverify', e);
// TODO: only retry if there is another trusted promise available...
// OR always reject?
await tryResolveTrustedPromise();
return;
}
}
if (this.trustedPromise !== origTrustedPromise) { // note: Promises that have already resolved will return the same value when awaited again :)
// we've been invalidated while we were waiting for the trusted result! const origTrustedPromise: Promise<T | null> | null = this.trustedPromise;
console.warn( let trustedResult = undefined;
'ULTRA RARE ALERT: we got unverified while awaiting a trusted promise another path was verifying!', try {
); trustedResult = await origTrustedPromise;
} catch (e: unknown) {
if (this.trustedPromise === origTrustedPromise) {
// TODO: decide if can unverify here rather than in the "catch-all"
// maybe this will let us get rid of the catch all?
throw e;
} else {
console.warn('trusted promise from another path rejected after unverify', e);
// TODO: only retry if there is another trusted promise available...
// OR always reject?
await tryResolveTrustedPromise(); await tryResolveTrustedPromise();
return; return;
} }
await this.verifyPromise; // we don't care about the result, just that we wait for the verification to finish
if (this.trustedPromise !== origTrustedPromise) {
// we've been invalidated while we were waiting for the verify!
console.warn(
'ULTRA RARE ALERT: we got unverified while awaiting a verify promise another path was calling!',
);
// ** complexity:
// if primaryUpToDate is false or we got unverified during verification, this path will still return
// the trustedResult that was passed to the verify function
}
if (!resolved) {
resolve(trustedResult);
resolved = true;
}
} }
} else {
// we are all up to date, make sure to resolve if primaryResult is null if (this.trustedPromise !== origTrustedPromise) {
// we've been invalidated while we were waiting for the trusted result!
console.warn(
'ULTRA RARE ALERT: we got unverified while awaiting a trusted promise another path was verifying!',
);
await tryResolveTrustedPromise();
return;
}
await this.verifyPromise; // we don't care about the result, just that we wait for the verification to finish
if (this.trustedPromise !== origTrustedPromise) {
// we've been invalidated while we were waiting for the verify!
console.warn(
'ULTRA RARE ALERT: we got unverified while awaiting a verify promise another path was calling!',
);
// ** complexity:
// if primaryUpToDate is false or we got unverified during verification, this path will still return
// the trustedResult that was passed to the verify function
}
if (!resolved) { if (!resolved) {
resolve(null); resolve(trustedResult);
resolved = true; resolved = true;
} }
} }