@tmlmobilidade/go-utils-exec 20260905.2252.20 → 20260906.1643.56
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.
|
@@ -44,12 +44,11 @@ interface BatchWriterParams<T> {
|
|
|
44
44
|
}
|
|
45
45
|
export declare class BatchWriter<T> {
|
|
46
46
|
private params;
|
|
47
|
-
private
|
|
48
|
-
private
|
|
47
|
+
private dataBucket;
|
|
48
|
+
private flushInProgress;
|
|
49
49
|
private batchTimeoutTimer;
|
|
50
50
|
private idleTimeoutTimer;
|
|
51
51
|
private sessionTimer;
|
|
52
|
-
private flushInProgress;
|
|
53
52
|
constructor(params: BatchWriterParams<T>);
|
|
54
53
|
/**
|
|
55
54
|
* Flushes the current batch of data.
|
|
@@ -5,15 +5,11 @@ import { Timer } from '@tmlmobilidade/timer';
|
|
|
5
5
|
export class BatchWriter {
|
|
6
6
|
//
|
|
7
7
|
params;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
dataBucket = [];
|
|
9
|
+
flushInProgress = null;
|
|
10
10
|
batchTimeoutTimer = null;
|
|
11
11
|
idleTimeoutTimer = null;
|
|
12
12
|
sessionTimer = new Timer();
|
|
13
|
-
// ponytail: single in-flight guard, not a queue. Serializes overlapping
|
|
14
|
-
// flushes (timer-triggered vs batch-full) so inserts never run concurrently
|
|
15
|
-
// and callers applying backpressure await the same promise.
|
|
16
|
-
flushInProgress = null;
|
|
17
13
|
constructor(params) {
|
|
18
14
|
if (!params.title)
|
|
19
15
|
throw new Error('BATCHWRITER: Title is required.');
|
|
@@ -54,58 +50,40 @@ export class BatchWriter {
|
|
|
54
50
|
*/
|
|
55
51
|
async drain(callback) {
|
|
56
52
|
await this.flush(callback);
|
|
57
|
-
if (this.
|
|
53
|
+
if (this.dataBucket.length > 0 && !this.idleTimeoutTimer && !this.batchTimeoutTimer && !this.flushInProgress) {
|
|
58
54
|
await this.flush(callback);
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
57
|
async runFlush(callback) {
|
|
58
|
+
//
|
|
59
|
+
//
|
|
60
|
+
// Invalidate all timers since a flush operation is being performed
|
|
61
|
+
if (this.idleTimeoutTimer) {
|
|
62
|
+
clearTimeout(this.idleTimeoutTimer);
|
|
63
|
+
this.idleTimeoutTimer = null;
|
|
64
|
+
}
|
|
65
|
+
if (this.batchTimeoutTimer) {
|
|
66
|
+
clearTimeout(this.batchTimeoutTimer);
|
|
67
|
+
this.batchTimeoutTimer = null;
|
|
68
|
+
}
|
|
69
|
+
//
|
|
70
|
+
// Skip if there is no data to flush
|
|
71
|
+
if (this.dataBucket.length === 0)
|
|
72
|
+
return;
|
|
73
|
+
// Transfer ownership of the current batch.
|
|
74
|
+
const batch = this.dataBucket;
|
|
75
|
+
this.dataBucket = [];
|
|
62
76
|
try {
|
|
63
|
-
//
|
|
64
77
|
const flushTimer = new Timer();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
this.idleTimeoutTimer = null;
|
|
71
|
-
}
|
|
72
|
-
if (this.batchTimeoutTimer) {
|
|
73
|
-
clearTimeout(this.batchTimeoutTimer);
|
|
74
|
-
this.batchTimeoutTimer = null;
|
|
75
|
-
}
|
|
76
|
-
//
|
|
77
|
-
// Skip if there is no data to flush
|
|
78
|
-
if (this.dataBucketAlwaysAvailable.length === 0)
|
|
79
|
-
return;
|
|
80
|
-
//
|
|
81
|
-
// Copy everything in dataBucketAlwaysAvailable to dataBucketFlushOps
|
|
82
|
-
// to prevent any new incoming data to be added to the batch. This is to ensure
|
|
83
|
-
// that the batch is not modified while it is being processed.
|
|
84
|
-
this.dataBucketFlushOps = [...this.dataBucketFlushOps, ...this.dataBucketAlwaysAvailable];
|
|
85
|
-
this.dataBucketAlwaysAvailable = [];
|
|
86
|
-
//
|
|
87
|
-
// Process the data for batch insert
|
|
88
|
-
try {
|
|
89
|
-
// Call the insert function provided in the params to perform the actual database insertion.
|
|
90
|
-
if (!this.params.insertFn)
|
|
91
|
-
throw new Error('BATCHWRITER: No insert function provided in params');
|
|
92
|
-
await this.insertWithRetry(this.dataBucketFlushOps);
|
|
93
|
-
console.info(`BATCHWRITER [${this.params.title}]: Flush | Length: ${this.dataBucketFlushOps.length} (session: ${sessionTimerResult}) (flush: ${flushTimer.get()})`);
|
|
94
|
-
// Call the flush callback, if provided
|
|
95
|
-
if (callback)
|
|
96
|
-
await callback(this.dataBucketFlushOps);
|
|
97
|
-
// Reset the flush bucket
|
|
98
|
-
this.dataBucketFlushOps = [];
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
console.error(`BATCHWRITER [${this.params.title}]: Error @ flush().insert(): ${error.message}`);
|
|
102
|
-
throw error; // Re-throw to allow retry logic at higher level
|
|
103
|
-
}
|
|
104
|
-
//
|
|
78
|
+
await this.insertWithRetry(batch);
|
|
79
|
+
console.info(`BATCHWRITER [${this.params.title}]: Flush | Length: ${batch.length} (session: ${this.sessionTimer.get()}) (flush: ${flushTimer.get()})`);
|
|
80
|
+
// Call the flush callback, if provided
|
|
81
|
+
if (callback)
|
|
82
|
+
await callback(batch);
|
|
105
83
|
}
|
|
106
84
|
catch (error) {
|
|
107
85
|
console.error(`BATCHWRITER [${this.params.title}]: Error @ flush(): ${error.message}`);
|
|
108
|
-
throw error;
|
|
86
|
+
throw error;
|
|
109
87
|
}
|
|
110
88
|
}
|
|
111
89
|
/**
|
|
@@ -118,13 +96,15 @@ export class BatchWriter {
|
|
|
118
96
|
*/
|
|
119
97
|
async insertWithRetry(data) {
|
|
120
98
|
const maxRetries = this.params.max_retries ?? 3;
|
|
121
|
-
const retryBaseDelayMs = this.params.retry_base_delay_ms ??
|
|
99
|
+
const retryBaseDelayMs = this.params.retry_base_delay_ms ?? 1_000;
|
|
122
100
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
123
101
|
try {
|
|
124
102
|
await this.params.insertFn(data);
|
|
125
103
|
return;
|
|
126
104
|
}
|
|
127
105
|
catch (error) {
|
|
106
|
+
if (attempt === maxRetries)
|
|
107
|
+
throw error;
|
|
128
108
|
const parsedError = error;
|
|
129
109
|
const nextAttempt = attempt + 1;
|
|
130
110
|
const delayMs = retryBaseDelayMs * (2 ** attempt);
|
|
@@ -152,23 +132,24 @@ export class BatchWriter {
|
|
|
152
132
|
//
|
|
153
133
|
// Check if the batch is full
|
|
154
134
|
const batchSize = this.params.batch_size ?? 10_000;
|
|
155
|
-
if (this.
|
|
135
|
+
if (this.dataBucket.length >= batchSize) {
|
|
156
136
|
console.info(`BATCHWRITER [${this.params.title}]: Batch full. Flushing data...`);
|
|
157
137
|
await this.flush(flushCallback);
|
|
158
138
|
}
|
|
159
139
|
//
|
|
160
140
|
// Reset the session timer (for logging purposes)
|
|
161
|
-
if (this.
|
|
141
|
+
if (this.dataBucket.length === 0) {
|
|
162
142
|
this.sessionTimer.reset();
|
|
163
143
|
}
|
|
164
144
|
//
|
|
165
145
|
// Add the current data to the batch
|
|
166
146
|
if (Array.isArray(data)) {
|
|
167
|
-
const
|
|
168
|
-
|
|
147
|
+
for (const item of data) {
|
|
148
|
+
this.dataBucket.push(item);
|
|
149
|
+
}
|
|
169
150
|
}
|
|
170
151
|
else {
|
|
171
|
-
this.
|
|
152
|
+
this.dataBucket.push(data);
|
|
172
153
|
}
|
|
173
154
|
//
|
|
174
155
|
// Call the write callback, if provided
|