@getanthill/datastore
Version:
Event-Sourced Datastore
118 lines (103 loc) • 3.28 kB
text/typescript
import type {
HandlerConfig,
RunnerServices,
Services,
Source,
} from '../../typings';
import { build } from '../../services';
import { random } from '../../utils';
function wait(delayInMilliseconds: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, delayInMilliseconds));
}
function blockEventLoop(delayInMilliseconds: number): void {
const tic = Date.now();
let blockedSince = 0;
while (blockedSince < delayInMilliseconds) {
blockedSince = Date.now() - tic;
}
}
export async function main(
url: URL,
services: Services = build(),
): Promise<HandlerConfig> {
const datastore = url.searchParams.get('datastore') ?? 'models';
const model = url.searchParams.get('model') ?? 'all';
const source = (url.searchParams.get('source') ?? 'events') as Source;
const query = JSON.parse(url.searchParams.get('query') ?? '{}');
const timeout = Number.parseInt(url.searchParams.get('timeout') ?? '0', 10);
const block = Number.parseInt(url.searchParams.get('block') ?? '0', 10);
const exception = Number.parseInt(
url.searchParams.get('exception') ?? '0',
10,
);
const progress = Number.parseInt(
url.searchParams.get('progress') ?? '1000',
10,
);
const withHeartbeat = url.searchParams.get('with_heartbeat') === 'true';
const withFetch = url.searchParams.get('with_fetch') === 'true';
const stats = {
processed: 0,
processing: 0,
exception: 0,
waiting: 0,
heartbeat: 0,
fetching: 0,
blocking: 0,
};
return {
triggers: [
{
datastore,
model,
source,
raw: false,
query,
},
],
start: async () => {
services.telemetry.logger.info('[utils#log] Starting');
return services as unknown as RunnerServices;
},
stop: async () => {
services.telemetry.logger.info('[utils#log] Ending');
},
handler: async (event: any) => {
stats.processing += 1;
services.telemetry.logger.debug('[utils#log] Event', event);
if (timeout > 0) {
stats.waiting += 1;
services.telemetry.logger.debug('[utils#log] Waiting', stats);
await wait(timeout);
stats.waiting -= 1;
}
if (withHeartbeat === true) {
stats.heartbeat += 1;
services.telemetry.logger.debug('[utils#log] Heartbeat', stats);
await services.datastores.get(datastore)?.heartbeat();
stats.heartbeat -= 1;
}
if (withFetch === true) {
stats.fetching += 1;
services.telemetry.logger.debug('[utils#log] Fetch', stats);
await services.datastores.get(datastore)?.find(model, {}, 0, 1000);
stats.fetching -= 1;
}
if (block > 0) {
stats.blocking += 1;
services.telemetry.logger.debug('[utils#log] Block', stats);
blockEventLoop(block);
stats.blocking -= 1;
}
if (exception > 0 && random() <= exception) {
stats.exception += 1;
services.telemetry.logger.debug('[utils#log] Exception', stats);
throw new Error('This is an error');
}
stats.processing -= 1;
stats.processed += 1;
stats.processed % progress === 0 &&
services.telemetry.logger.info('[utils#log] Handled', stats);
},
};
}