Skip to content

Commit

Permalink
add ability to schedule a single job by ref (#2165)
Browse files Browse the repository at this point in the history
* add ability to schedule a single job by ref

Signed-off-by: Brian Fletcher <[email protected]>

* add api report changes

Signed-off-by: Brian Fletcher <[email protected]>

---------

Signed-off-by: Brian Fletcher <[email protected]>
  • Loading branch information
punkle authored Dec 11, 2024
1 parent 947c56b commit 306121a
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 29 deletions.
5 changes: 5 additions & 0 deletions workspaces/tech-insights/.changeset/breezy-ducks-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@backstage-community/plugin-tech-insights-backend': minor
---

Allow tech insights backend to schedule a single job.
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ export const entityMetadataFactRetriever: FactRetriever;
export const entityOwnershipFactRetriever: FactRetriever;

// @public
export interface FactRetrieverEngine {
getJobRegistration(ref: string): Promise<FactRetrieverRegistration>;
schedule(): Promise<void>;
triggerJob(ref: string): Promise<void>;
export abstract class FactRetrieverEngine {
abstract getJobRegistration(ref: string): Promise<FactRetrieverRegistration>;
abstract schedule(): Promise<void>;
scheduleJob(_: string): Promise<void>;
abstract triggerJob(ref: string): Promise<void>;
}

// @public (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,37 @@ function duration(startTimestamp: [number, number]): string {
*
* FactRetrieverEngine responsible scheduling and running fact retrieval tasks.
*/
export interface FactRetrieverEngine {
export abstract class FactRetrieverEngine {
/**
* Schedules fact retriever run cycles based on configuration provided in the registration.
*
* Default implementation uses backend-tasks to handle scheduling. This function can be called multiple
* times, where initial calls schedule the tasks and subsequent invocations update the schedules.
*/
schedule(): Promise<void>;
abstract schedule(): Promise<void>;

/**
* Schedules single fact retriever run cycles based on configuration provided in the registration.
*
* Default implementation defers to scheduling all jobs
*/
async scheduleJob(_: string): Promise<void> {
return await this.schedule();
}

/**
* Provides possibility to manually run a fact retriever job and construct fact data
*
* @param ref - Reference to the task name stored in the executor database. By convention this is the fact retriever id
*/
triggerJob(ref: string): Promise<void>;
abstract triggerJob(ref: string): Promise<void>;

/**
* Exposes fact retriever job configuration information about previous and next runs and schedule
*
* @param ref - Reference to the task name stored in the executor database. By convention this is the fact retriever id
*/
getJobRegistration(ref: string): Promise<FactRetrieverRegistration>;
abstract getJobRegistration(ref: string): Promise<FactRetrieverRegistration>;
}

export class DefaultFactRetrieverEngine implements FactRetrieverEngine {
Expand Down Expand Up @@ -112,36 +121,45 @@ export class DefaultFactRetrieverEngine implements FactRetrieverEngine {
);
}

private async scheduleRegistration(registration: FactRetrieverRegistration) {
const { factRetriever, cadence, lifecycle, timeout, initialDelay } =
registration;
const cronExpression = cadence || this.defaultCadence || randomDailyCron();
const timeLimit =
timeout || this.defaultTimeout || Duration.fromObject({ minutes: 5 });
const initialDelaySetting =
initialDelay ||
this.defaultInitialDelay ||
Duration.fromObject({ seconds: 5 });

await this.scheduler.scheduleTask({
id: factRetriever.id,
frequency: { cron: cronExpression },
fn: this.createFactRetrieverHandler(factRetriever, lifecycle),
timeout: timeLimit,
// We add a delay in order to prevent errors due to the
// fact that the backend is not yet online in a cold-start scenario
initialDelay: initialDelaySetting,
});
}

async scheduleJob(ref: string): Promise<void> {
const registration = await this.factRetrieverRegistry.get(ref);
return this.scheduleRegistration(registration);
}

async schedule() {
const registrations = await this.factRetrieverRegistry.listRegistrations();
const newRegs: string[] = [];

await Promise.all(
registrations.map(async registration => {
const { factRetriever, cadence, lifecycle, timeout, initialDelay } =
registration;
const cronExpression =
cadence || this.defaultCadence || randomDailyCron();
const timeLimit =
timeout || this.defaultTimeout || Duration.fromObject({ minutes: 5 });
const initialDelaySetting =
initialDelay ||
this.defaultInitialDelay ||
Duration.fromObject({ seconds: 5 });
try {
await this.scheduler.scheduleTask({
id: factRetriever.id,
frequency: { cron: cronExpression },
fn: this.createFactRetrieverHandler(factRetriever, lifecycle),
timeout: timeLimit,
// We add a delay in order to prevent errors due to the
// fact that the backend is not yet online in a cold-start scenario
initialDelay: initialDelaySetting,
});
newRegs.push(factRetriever.id);
await this.scheduleRegistration(registration);
newRegs.push(registration.factRetriever.id);
} catch (e) {
this.logger.warn(
`Failed to schedule fact retriever ${factRetriever.id}, ${e}`,
`Failed to schedule fact retriever ${registration.factRetriever.id}, ${e}`,
);
}
}),
Expand Down

0 comments on commit 306121a

Please sign in to comment.