snowpipe-streaming 1.6.2 → 1.7.1
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 +7 -7
- package/src/elastic_channel.js +34 -19
- package/src/index.d.ts +12 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snowpipe-streaming",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Snowflake Streaming Ingest SDK for Node.js",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"typedoc": "^0.28.19"
|
|
28
28
|
},
|
|
29
29
|
"optionalDependencies": {
|
|
30
|
-
"snowpipe-streaming-linux-x64-gnu": "1.
|
|
31
|
-
"snowpipe-streaming-linux-x64-musl": "1.
|
|
32
|
-
"snowpipe-streaming-linux-arm64-gnu": "1.
|
|
33
|
-
"snowpipe-streaming-linux-arm64-musl": "1.
|
|
34
|
-
"snowpipe-streaming-darwin-arm64": "1.
|
|
35
|
-
"snowpipe-streaming-win32-x64-msvc": "1.
|
|
30
|
+
"snowpipe-streaming-linux-x64-gnu": "1.7.1",
|
|
31
|
+
"snowpipe-streaming-linux-x64-musl": "1.7.1",
|
|
32
|
+
"snowpipe-streaming-linux-arm64-gnu": "1.7.1",
|
|
33
|
+
"snowpipe-streaming-linux-arm64-musl": "1.7.1",
|
|
34
|
+
"snowpipe-streaming-darwin-arm64": "1.7.1",
|
|
35
|
+
"snowpipe-streaming-win32-x64-msvc": "1.7.1"
|
|
36
36
|
}
|
|
37
37
|
}
|
package/src/elastic_channel.js
CHANGED
|
@@ -43,6 +43,12 @@ class StreamingIngestElasticChannel {
|
|
|
43
43
|
}
|
|
44
44
|
this._native = nativeChannel;
|
|
45
45
|
this._pendingFutures = pendingFutures;
|
|
46
|
+
// Per-channel future id counter; the wrapper allocates ids so it can register
|
|
47
|
+
// the promise before the row becomes ack-eligible. The single-threaded event
|
|
48
|
+
// loop makes the plain increment safe. The id is a JS number (a safe integer,
|
|
49
|
+
// exact to 2^53) marshaled to the Rust i64.
|
|
50
|
+
// Starts at 1; 0 is reserved as the "no future id" sentinel, matching the other SDKs.
|
|
51
|
+
this._nextFutureId = 1;
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
/** @returns {string} The channel name (always "ELASTIC") */
|
|
@@ -93,10 +99,10 @@ class StreamingIngestElasticChannel {
|
|
|
93
99
|
/**
|
|
94
100
|
* Append multiple rows into the elastic channel.
|
|
95
101
|
*
|
|
96
|
-
* The
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* the server acknowledgement (or rejects on error
|
|
102
|
+
* The wrapper allocates the future id and registers its promise in the shared
|
|
103
|
+
* pending map before the synchronous native append, so the ack can never
|
|
104
|
+
* precede registration. The Promise resolves later when the mark-complete
|
|
105
|
+
* callback fires with the server acknowledgement (or rejects on error).
|
|
100
106
|
*
|
|
101
107
|
* @param {Object[]} rows - Array of row objects
|
|
102
108
|
* @returns {Promise<void>} Resolves when Snowflake acknowledges the batch
|
|
@@ -111,18 +117,13 @@ class StreamingIngestElasticChannel {
|
|
|
111
117
|
// Empty arrays are validated by the Rust core, which throws a proper error.
|
|
112
118
|
const buffer =
|
|
113
119
|
rows.length === 0 ? Buffer.alloc(0) : serializeRowsToNdjson(rows, this.binaryInputFormat);
|
|
114
|
-
let futureId;
|
|
115
|
-
try {
|
|
116
|
-
futureId = Number(this._native.appendRows(buffer, rows.length));
|
|
117
|
-
} catch (e) {
|
|
118
|
-
throw StreamingIngestError.from(e);
|
|
119
|
-
}
|
|
120
120
|
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
121
|
+
// Allocate the id and register the promise BEFORE the native append, so the
|
|
122
|
+
// row cannot become ack-eligible before its promise is in the pending map.
|
|
123
|
+
// Building the promise out-of-line lets us stash {resolve, reject} into the
|
|
124
|
+
// shared map synchronously; the single-threaded event loop guarantees this
|
|
125
|
+
// happens-before any queued threadsafe-function callback runs.
|
|
126
|
+
const futureId = this._nextFutureId++;
|
|
126
127
|
let resolveFn;
|
|
127
128
|
let rejectFn;
|
|
128
129
|
const promise = new Promise((resolve, reject) => {
|
|
@@ -132,10 +133,7 @@ class StreamingIngestElasticChannel {
|
|
|
132
133
|
const previous = this._pendingFutures.get(futureId);
|
|
133
134
|
this._pendingFutures.set(futureId, { resolve: resolveFn, reject: rejectFn });
|
|
134
135
|
if (previous !== undefined) {
|
|
135
|
-
//
|
|
136
|
-
// and the core have drifted, which is fatal. Reject the displaced
|
|
137
|
-
// promise, invalidate the channel, and throw to the caller so the
|
|
138
|
-
// new promise we just stashed isn't left hanging either.
|
|
136
|
+
// Monotonic per-channel ids make this a should-never-happen invariant.
|
|
139
137
|
const errMsg = `Duplicate future_id=${futureId} in pendingFutures`;
|
|
140
138
|
const err = new StreamingIngestError("Fatal", errMsg, 500, "Internal Server Error");
|
|
141
139
|
previous.reject(err);
|
|
@@ -147,6 +145,14 @@ class StreamingIngestElasticChannel {
|
|
|
147
145
|
}
|
|
148
146
|
throw err;
|
|
149
147
|
}
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
this._native.appendRows(buffer, rows.length, futureId);
|
|
151
|
+
} catch (e) {
|
|
152
|
+
// Never enqueued (not ack-eligible), so no ack can arrive; drop the promise.
|
|
153
|
+
this._pendingFutures.delete(futureId);
|
|
154
|
+
throw StreamingIngestError.from(e);
|
|
155
|
+
}
|
|
150
156
|
return promise;
|
|
151
157
|
}
|
|
152
158
|
|
|
@@ -162,6 +168,15 @@ class StreamingIngestElasticChannel {
|
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Wait for this channel to flush.
|
|
173
|
+
* @param {{timeoutMs?: number}} [options]
|
|
174
|
+
* @returns {Promise<void>}
|
|
175
|
+
*/
|
|
176
|
+
async waitForFlush(options) {
|
|
177
|
+
return callFFI(() => this._native.waitForFlush(options?.timeoutMs));
|
|
178
|
+
}
|
|
179
|
+
|
|
165
180
|
/**
|
|
166
181
|
* Get the current status of this channel.
|
|
167
182
|
* @returns {Promise<ChannelStatus>}
|
package/src/index.d.ts
CHANGED
|
@@ -380,6 +380,18 @@ export class StreamingIngestElasticChannel {
|
|
|
380
380
|
*/
|
|
381
381
|
initiateFlush(): void;
|
|
382
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Wait for all buffered data in this channel to be flushed to Snowflake.
|
|
385
|
+
*
|
|
386
|
+
* @param options - Wait options
|
|
387
|
+
* @throws StreamingIngestError If waiting for the flush fails
|
|
388
|
+
* @throws Error If the timeout is reached
|
|
389
|
+
*/
|
|
390
|
+
waitForFlush(options?: {
|
|
391
|
+
/** Optional timeout in milliseconds. */
|
|
392
|
+
timeoutMs?: number;
|
|
393
|
+
}): Promise<void>;
|
|
394
|
+
|
|
383
395
|
/**
|
|
384
396
|
* Get the current status of this channel.
|
|
385
397
|
*
|