@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 dataBucketAlwaysAvailable;
48
- private dataBucketFlushOps;
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
- dataBucketAlwaysAvailable = [];
9
- dataBucketFlushOps = [];
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.dataBucketAlwaysAvailable.length > 0 && !this.idleTimeoutTimer && !this.batchTimeoutTimer && !this.flushInProgress) {
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
- const sessionTimerResult = this.sessionTimer.get();
66
- //
67
- // Invalidate all timers since a flush operation is being performed
68
- if (this.idleTimeoutTimer) {
69
- clearTimeout(this.idleTimeoutTimer);
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; // Re-throw to allow retry logic at higher level
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 ?? 1000;
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.dataBucketAlwaysAvailable.length >= batchSize) {
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.dataBucketAlwaysAvailable.length === 0) {
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 combinedDataWithOptions = data.map(item => item);
168
- this.dataBucketAlwaysAvailable = [...this.dataBucketAlwaysAvailable, ...combinedDataWithOptions];
147
+ for (const item of data) {
148
+ this.dataBucket.push(item);
149
+ }
169
150
  }
170
151
  else {
171
- this.dataBucketAlwaysAvailable.push(data);
152
+ this.dataBucket.push(data);
172
153
  }
173
154
  //
174
155
  // Call the write callback, if provided
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmlmobilidade/go-utils-exec",
3
- "version": "20260905.2252.20",
3
+ "version": "20260906.1643.56",
4
4
  "author": {
5
5
  "email": "iso@tmlmobilidade.pt",
6
6
  "name": "TML-ISO"