Add listAncestors method, simplify refresh

Co-authored-by: Patrik Oldsberg <poldsberg@gmail.com>
Signed-off-by: Johan Haals <johan.haals@gmail.com>
This commit is contained in:
Johan Haals
2021-09-13 15:21:51 +02:00
parent 5c8c5ae1e4
commit 76ebabc730
3 changed files with 47 additions and 16 deletions
@@ -242,7 +242,16 @@ export class DefaultCatalogProcessingEngine implements CatalogProcessingEngine {
async refresh(options: EntityRefreshOptions) {
await this.processingDatabase.transaction(async tx => {
await this.processingDatabase.refresh(tx, options);
const { entityRefs } = await this.processingDatabase.listAncestors(tx, {
entityRef: options.entityRef,
});
const locationAncestor = entityRefs.find(ref =>
ref.startsWith('location:'),
);
await this.processingDatabase.refresh(tx, {
entityRef: locationAncestor ?? options.entityRef,
});
});
}
}
@@ -39,6 +39,8 @@ import {
RefreshOptions,
ReplaceUnprocessedEntitiesOptions,
UpdateProcessedEntityOptions,
ListAncestorsOptions,
ListAncestorsResult,
} from './types';
// The number of items that are sent per batch to the database layer, when
@@ -46,7 +48,7 @@ import {
// errors in the underlying engine due to exceeding query limits, but large
// enough to get the speed benefits.
const BATCH_SIZE = 50;
const MAX_REFRESH_DEPTH = 10;
const MAX_ANCESTOR_DEPTH = 32;
export class DefaultProcessingDatabase implements ProcessingDatabase {
constructor(
@@ -516,18 +518,20 @@ export class DefaultProcessingDatabase implements ProcessingDatabase {
};
}
async refresh(txOpaque: Transaction, options: RefreshOptions): Promise<void> {
async listAncestors(
txOpaque: Transaction,
options: ListAncestorsOptions,
): Promise<ListAncestorsResult> {
const tx = txOpaque as Knex.Transaction;
const { entityRef } = options;
let refreshTarget = entityRef;
const entityRefs = new Array<string>();
let currentRef = entityRef;
let depth = 0;
for (;;) {
if (depth++ > MAX_REFRESH_DEPTH) {
if (depth++ > MAX_ANCESTOR_DEPTH) {
throw new Error(
`Unable to refresh Entity ${entityRef}, maximum refresh depth of ${MAX_REFRESH_DEPTH} reached`,
`Unable receive ancestors for ${entityRef}, reached maximum depth of ${MAX_ANCESTOR_DEPTH}`,
);
}
@@ -550,22 +554,22 @@ export class DefaultProcessingDatabase implements ProcessingDatabase {
if (!parentRef) {
// We've reached the top of the tree which is the entityProvider.
// In this case we refresh the entity itself.
break;
}
if (parentRef.startsWith('location:')) {
refreshTarget = parentRef;
break;
return { entityRefs };
}
entityRefs.push(parentRef);
currentRef = parentRef;
}
}
async refresh(txOpaque: Transaction, options: RefreshOptions): Promise<void> {
const tx = txOpaque as Knex.Transaction;
const { entityRef } = options;
const updateResult = await tx<DbRefreshStateRow>('refresh_state')
.where({ entity_ref: refreshTarget })
.where({ entity_ref: entityRef })
.update({ next_update_at: tx.fn.now() });
if (updateResult === 0) {
throw new ConflictError(
`Failed to schedule ${refreshTarget} for refresh`,
);
throw new ConflictError(`Failed to schedule ${entityRef} for refresh`);
}
}
@@ -83,6 +83,14 @@ export type RefreshOptions = {
entityRef: string;
};
export type ListAncestorsOptions = {
entityRef: string;
};
export type ListAncestorsResult = {
entityRefs: string[];
};
export interface ProcessingDatabase {
transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T>;
@@ -122,4 +130,14 @@ export interface ProcessingDatabase {
* Schedules a refresh of a given entityRef.
*/
refresh(txOpaque: Transaction, options: RefreshOptions): Promise<void>;
/**
* Lists all ancestors of a given entityRef.
*
* The returned list is ordered from the most immediate ancestor to the most distant one.
*/
listAncestors(
txOpaque: Transaction,
options: ListAncestorsOptions,
): Promise<ListAncestorsResult>;
}