Skip to content

Commit 778111d

Browse files
tea-artistgithub-actions[bot]caoxing9boris-whammond-lj
authored
[sync] fix(computed): add failure diagnostics for outbox workers (#2836)
Synced from teableio/teable-ee@3d8d8f8 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Aries X <caoxing9@gmail.com> Co-authored-by: Boris <boris2code@outlook.com> Co-authored-by: Jun Lu <hammond@teable.io> Co-authored-by: nichenqin <nichenqin@hotmail.com>
1 parent 0f57409 commit 778111d

71 files changed

Lines changed: 1014 additions & 827 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/issue-id-check.yml

Lines changed: 0 additions & 250 deletions
This file was deleted.

apps/nestjs-backend/src/db-provider/select-query/postgres/select-query.postgres.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ describe('SelectQueryPostgres FROMNOW/TONOW', () => {
153153
});
154154

155155
describe('SelectQueryPostgres workday', () => {
156-
it('uses interval multiplication for dynamic day-count expressions', () => {
156+
it('generates CTE-based workday SQL that skips weekends and holidays', () => {
157157
const query = new SelectQueryPostgres();
158158
query.setContext({ timeZone: 'Asia/Shanghai' } as unknown as never);
159159
query.setCallMetadata([
@@ -162,7 +162,9 @@ describe('SelectQueryPostgres workday', () => {
162162
] as unknown as never);
163163

164164
const sql = query.workday('"t"."Date"', '"t"."Number"');
165-
expect(sql).toContain(`INTERVAL '1 day' * ("t"."Number")::double precision`);
166-
expect(sql).not.toContain(" days'");
165+
expect(sql).toContain('WITH params AS');
166+
expect(sql).toContain('generate_series');
167+
expect(sql).toContain('EXTRACT(DOW FROM c.candidate_date)');
168+
expect(sql).toContain(`("t"."Number")::double precision`);
167169
});
168170
});

apps/nestjs-backend/src/event-emitter/event-job/fallback/fallback-queue.service.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import type { OnModuleInit } from '@nestjs/common';
22
import { Injectable, Logger } from '@nestjs/common';
33
import { Reflector, DiscoveryService } from '@nestjs/core';
44
import type { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
5-
import type { Job } from 'bullmq';
65
import { localQueueEventEmitter } from './event-emitter';
6+
import type { ILocalJob } from './local-queue.provider';
77

88
export const PROCESSOR_METADATA = 'bullmq:processor_metadata';
99

@@ -43,7 +43,7 @@ export class FallbackQueueService implements OnModuleInit {
4343
instance.constructor || metatype
4444
);
4545
localQueueEventEmitter.removeAllListeners(`handle-listener-${queueName}`);
46-
localQueueEventEmitter.on(`handle-listener-${queueName}`, (job: Job<unknown>) => {
46+
localQueueEventEmitter.on(`handle-listener-${queueName}`, (job: ILocalJob) => {
4747
if (job.queueName !== queueName) {
4848
return;
4949
}
@@ -55,7 +55,7 @@ export class FallbackQueueService implements OnModuleInit {
5555
private async handleListener(
5656
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5757
wrapper: InstanceWrapper,
58-
job: Job<unknown>
58+
job: ILocalJob
5959
) {
6060
const { instance } = wrapper;
6161
const methodName = 'process';
@@ -64,8 +64,13 @@ export class FallbackQueueService implements OnModuleInit {
6464
return;
6565
}
6666
try {
67-
await instance[methodName].call(instance, job);
67+
job.state = 'active';
68+
const result = await instance[methodName].call(instance, job);
69+
job.state = 'completed';
70+
job.returnvalue = result;
6871
} catch (error) {
72+
job.state = 'failed';
73+
job.failedReason = error instanceof Error ? error.message : String(error);
6974
this.logger.error(`Error processing job ${job.name}:`, error);
7075
}
7176
}

apps/nestjs-backend/src/event-emitter/event-job/fallback/local-queue.provider.ts

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,66 @@ import { getRandomString } from '@teable/core';
44
import type { JobsOptions } from 'bullmq';
55
import { localQueueEventEmitter } from './event-emitter';
66

7+
export interface ILocalJob {
8+
id: string;
9+
name: string;
10+
data: unknown;
11+
opts?: JobsOptions;
12+
queueName: string;
13+
progress: number | object;
14+
returnvalue: unknown;
15+
failedReason?: string;
16+
state: string;
17+
getState: () => Promise<string>;
18+
updateProgress: (progress: number | object) => Promise<void>;
19+
}
20+
721
export const createLocalQueueProvider = (queueName: string): Provider => ({
822
provide: getQueueToken(queueName),
923
useFactory: async () => {
24+
const jobs = new Map<string, ILocalJob>();
25+
26+
const createJob = (id: string, name: string, data: unknown, opts?: JobsOptions): ILocalJob => {
27+
const job: ILocalJob = {
28+
id,
29+
name,
30+
data,
31+
opts,
32+
queueName,
33+
progress: 0,
34+
returnvalue: undefined,
35+
failedReason: undefined,
36+
state: 'waiting',
37+
getState: async () => job.state,
38+
updateProgress: async (p: number | object) => {
39+
job.progress = p;
40+
},
41+
};
42+
return job;
43+
};
44+
1045
return {
1146
add: (name: string, data: unknown, opts?: JobsOptions) => {
12-
localQueueEventEmitter.emit(`handle-listener-${queueName}`, {
13-
id: getRandomString(10),
14-
name,
15-
data,
16-
opts,
17-
queueName,
18-
});
47+
const id = opts?.jobId ?? getRandomString(10);
48+
const job = createJob(id, name, data, opts);
49+
jobs.set(id, job);
50+
localQueueEventEmitter.emit(`handle-listener-${queueName}`, job);
51+
return job;
1952
},
20-
addBulk: (jobs: JobsOptions[]) => {
21-
jobs.forEach((job) => {
53+
addBulk: (bulkJobs: JobsOptions[]) => {
54+
bulkJobs.forEach((job) => {
2255
localQueueEventEmitter.emit(`handle-listener-${queueName}`, job);
2356
});
2457
},
58+
getJob: async (jobId: string) => {
59+
return jobs.get(jobId) ?? null;
60+
},
61+
getJobs: async () => {
62+
return Array.from(jobs.values());
63+
},
64+
getJobCountByTypes: async () => {
65+
return jobs.size;
66+
},
2567
};
2668
},
2769
});

0 commit comments

Comments
 (0)