@output.ai/core 0.4.8 → 0.4.9-dev.pr326-21ac1cf
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -6,7 +6,8 @@ const envVarSchema = z.object( {
|
|
|
6
6
|
OUTPUT_AWS_SECRET_ACCESS_KEY: z.string(),
|
|
7
7
|
OUTPUT_TRACE_REMOTE_S3_BUCKET: z.string(),
|
|
8
8
|
OUTPUT_REDIS_URL: z.string(),
|
|
9
|
-
OUTPUT_REDIS_TRACE_TTL: z.coerce.number().int().positive().default( 60 * 60 * 24 * 7 ) // 7 days
|
|
9
|
+
OUTPUT_REDIS_TRACE_TTL: z.coerce.number().int().positive().default( 60 * 60 * 24 * 7 ), // 7 days
|
|
10
|
+
OUTPUT_TRACE_UPLOAD_DELAY_MS: z.coerce.number().int().nonnegative().default( 10_000 ) // 10s
|
|
10
11
|
} );
|
|
11
12
|
|
|
12
13
|
const env = {};
|
|
@@ -19,6 +20,7 @@ export const loadEnv = () => {
|
|
|
19
20
|
env.remoteS3Bucket = parsedFields.OUTPUT_TRACE_REMOTE_S3_BUCKET;
|
|
20
21
|
env.redisUrl = parsedFields.OUTPUT_REDIS_URL;
|
|
21
22
|
env.redisIncompleteWorkflowsTTL = parsedFields.OUTPUT_REDIS_TRACE_TTL;
|
|
23
|
+
env.traceUploadDelayMs = parsedFields.OUTPUT_TRACE_UPLOAD_DELAY_MS;
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
export const getVars = () => {
|
|
@@ -83,6 +83,12 @@ export const exec = async ( { entry, executionContext } ) => {
|
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
// Wait for straggler entries from other workers to land in Redis before uploading
|
|
87
|
+
const delayMs = getVars().traceUploadDelayMs;
|
|
88
|
+
if ( delayMs > 0 ) {
|
|
89
|
+
await new Promise( resolve => setTimeout( resolve, delayMs ) );
|
|
90
|
+
}
|
|
91
|
+
|
|
86
92
|
const content = buildTraceTree( await getEntries( cacheKey ) );
|
|
87
93
|
// if the trace tree is incomplete it will return null, in this case we can safely discard
|
|
88
94
|
if ( !content ) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
|
|
3
3
|
const loadEnvMock = vi.fn();
|
|
4
4
|
const getVarsMock = vi.fn( () => ( {
|
|
@@ -29,8 +29,13 @@ vi.mock( '../../tools/build_trace_tree.js', () => ( { default: buildTraceTreeMoc
|
|
|
29
29
|
|
|
30
30
|
describe( 'tracing/processors/s3', () => {
|
|
31
31
|
beforeEach( () => {
|
|
32
|
+
vi.useFakeTimers();
|
|
32
33
|
vi.clearAllMocks();
|
|
33
|
-
getVarsMock.mockReturnValue( { remoteS3Bucket: 'bkt', redisIncompleteWorkflowsTTL: 3600 } );
|
|
34
|
+
getVarsMock.mockReturnValue( { remoteS3Bucket: 'bkt', redisIncompleteWorkflowsTTL: 3600, traceUploadDelayMs: 10_000 } );
|
|
35
|
+
} );
|
|
36
|
+
|
|
37
|
+
afterEach( () => {
|
|
38
|
+
vi.useRealTimers();
|
|
34
39
|
} );
|
|
35
40
|
|
|
36
41
|
it( 'init(): loads config and ensures redis client is created', async () => {
|
|
@@ -57,8 +62,10 @@ describe( 'tracing/processors/s3', () => {
|
|
|
57
62
|
|
|
58
63
|
await exec( { ...ctx, entry: { name: 'A', phase: 'start', timestamp: startTime, parentId: 'root' } } );
|
|
59
64
|
await exec( { ...ctx, entry: { name: 'A', phase: 'tick', timestamp: startTime + 1, parentId: 'root' } } );
|
|
60
|
-
// Root end: no parentId and not start
|
|
61
|
-
|
|
65
|
+
// Root end: no parentId and not start — triggers the 10s delay before upload
|
|
66
|
+
const endPromise = exec( { ...ctx, entry: { name: 'A', phase: 'end', timestamp: startTime + 2 } } );
|
|
67
|
+
await vi.advanceTimersByTimeAsync( 10_000 );
|
|
68
|
+
await endPromise;
|
|
62
69
|
|
|
63
70
|
// Accumulation happened 3 times
|
|
64
71
|
expect( redisMulti.zAdd ).toHaveBeenCalledTimes( 3 );
|
|
@@ -78,7 +85,7 @@ describe( 'tracing/processors/s3', () => {
|
|
|
78
85
|
} );
|
|
79
86
|
|
|
80
87
|
it( 'getDestination(): returns S3 URL using bucket and key from getVars', async () => {
|
|
81
|
-
getVarsMock.mockReturnValue( { remoteS3Bucket: 'my-bucket', redisIncompleteWorkflowsTTL: 3600 } );
|
|
88
|
+
getVarsMock.mockReturnValue( { remoteS3Bucket: 'my-bucket', redisIncompleteWorkflowsTTL: 3600, traceUploadDelayMs: 10_000 } );
|
|
82
89
|
const { getDestination } = await import( './index.js' );
|
|
83
90
|
const startTime = Date.parse( '2020-01-02T03:04:05.678Z' );
|
|
84
91
|
const url = getDestination( { workflowId: 'id1', workflowName: 'WF', startTime } );
|
|
@@ -111,7 +118,9 @@ describe( 'tracing/processors/s3', () => {
|
|
|
111
118
|
zRangeMock.mockResolvedValue( [ JSON.stringify( { id: 'wf', phase: 'end', timestamp: startTime } ) ] );
|
|
112
119
|
buildTraceTreeMock.mockReturnValueOnce( null );
|
|
113
120
|
|
|
114
|
-
|
|
121
|
+
const endPromise = exec( { ...ctx, entry: { id: 'wf', phase: 'end', timestamp: startTime } } );
|
|
122
|
+
await vi.advanceTimersByTimeAsync( 10_000 );
|
|
123
|
+
await endPromise;
|
|
115
124
|
|
|
116
125
|
expect( buildTraceTreeMock ).toHaveBeenCalledTimes( 1 );
|
|
117
126
|
expect( uploadMock ).not.toHaveBeenCalled();
|