@adaptic/backend-legacy 0.0.960 → 0.0.962
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/ABTest.cjs +180 -66
- package/Account.cjs +180 -66
- package/AccountDecisionRecord.cjs +180 -66
- package/AccountLinkingRequest.cjs +180 -66
- package/Action.cjs +180 -66
- package/Alert.cjs +180 -66
- package/Allocation.cjs +180 -66
- package/AlpacaAccount.cjs +180 -66
- package/AnalyticsConfiguration.cjs +180 -66
- package/AnalyticsSnapshot.cjs +180 -66
- package/Asset.cjs +180 -66
- package/AuditLog.cjs +180 -66
- package/Authenticator.cjs +180 -66
- package/Configuration.cjs +180 -66
- package/ConflictEvent.cjs +180 -66
- package/ConnectionHealthSnapshot.cjs +180 -66
- package/Customer.cjs +180 -66
- package/DeadLetterMessage.cjs +180 -66
- package/DecisionMemorySummary.cjs +180 -66
- package/EconomicEvent.cjs +180 -66
- package/EquityBar.cjs +180 -66
- package/Event.cjs +180 -66
- package/EventSnapshot.cjs +180 -66
- package/FeatureImportanceAnalysis.cjs +180 -66
- package/InstitutionalFlowSignal.cjs +180 -66
- package/InstitutionalHolding.cjs +180 -66
- package/InstitutionalSentimentAlerts.cjs +180 -66
- package/InstitutionalSentimentErrors.cjs +180 -66
- package/InstitutionalSentimentHistory.cjs +180 -66
- package/InstitutionalSentimentMetrics.cjs +180 -66
- package/InviteToken.cjs +180 -66
- package/LinkedProvider.cjs +180 -66
- package/LlmConfiguration.cjs +180 -66
- package/MLModelVersion.cjs +180 -66
- package/MLTrainingData.cjs +180 -66
- package/MarketSentiment.cjs +180 -66
- package/ModelArtifact.cjs +180 -66
- package/ModelVersion.cjs +180 -66
- package/ModelVersionArtifact.cjs +180 -66
- package/NewsArticle.cjs +180 -66
- package/NewsArticleAssetSentiment.cjs +180 -66
- package/OptionsContract.cjs +180 -66
- package/OptionsGreeksHistory.cjs +180 -66
- package/OptionsPosition.cjs +180 -66
- package/OptionsPositionEvent.cjs +180 -66
- package/OptionsTradeExecution.cjs +180 -66
- package/PolicyOverlay.cjs +180 -66
- package/PortfolioGreeksHistory.cjs +180 -66
- package/ScheduledOptionOrder.cjs +180 -66
- package/Session.cjs +180 -66
- package/SignalGeneratorMetrics.cjs +180 -66
- package/SignalLineage.cjs +180 -66
- package/SignalOutcome.cjs +180 -66
- package/SignalPriorityQueue.cjs +180 -66
- package/SyncEvent.cjs +180 -66
- package/SystemAlert.cjs +180 -66
- package/Trade.cjs +180 -66
- package/TradeAuditEvent.cjs +180 -66
- package/TradeExecutionHistory.cjs +180 -66
- package/TradeOutcome.cjs +180 -66
- package/TradingPolicy.cjs +180 -66
- package/User.cjs +180 -66
- package/VerificationToken.cjs +180 -66
- package/WaitlistEntry.cjs +180 -66
- package/client.cjs +131 -9
- package/client.d.ts +32 -0
- package/esm/ABTest.d.ts.map +1 -1
- package/esm/ABTest.js.map +1 -1
- package/esm/ABTest.mjs +180 -66
- package/esm/Account.d.ts.map +1 -1
- package/esm/Account.js.map +1 -1
- package/esm/Account.mjs +180 -66
- package/esm/AccountDecisionRecord.d.ts.map +1 -1
- package/esm/AccountDecisionRecord.js.map +1 -1
- package/esm/AccountDecisionRecord.mjs +180 -66
- package/esm/AccountLinkingRequest.d.ts.map +1 -1
- package/esm/AccountLinkingRequest.js.map +1 -1
- package/esm/AccountLinkingRequest.mjs +180 -66
- package/esm/Action.d.ts.map +1 -1
- package/esm/Action.js.map +1 -1
- package/esm/Action.mjs +180 -66
- package/esm/Alert.d.ts.map +1 -1
- package/esm/Alert.js.map +1 -1
- package/esm/Alert.mjs +180 -66
- package/esm/Allocation.d.ts.map +1 -1
- package/esm/Allocation.js.map +1 -1
- package/esm/Allocation.mjs +180 -66
- package/esm/AlpacaAccount.d.ts.map +1 -1
- package/esm/AlpacaAccount.js.map +1 -1
- package/esm/AlpacaAccount.mjs +180 -66
- package/esm/AnalyticsConfiguration.d.ts.map +1 -1
- package/esm/AnalyticsConfiguration.js.map +1 -1
- package/esm/AnalyticsConfiguration.mjs +180 -66
- package/esm/AnalyticsSnapshot.d.ts.map +1 -1
- package/esm/AnalyticsSnapshot.js.map +1 -1
- package/esm/AnalyticsSnapshot.mjs +180 -66
- package/esm/Asset.d.ts.map +1 -1
- package/esm/Asset.js.map +1 -1
- package/esm/Asset.mjs +180 -66
- package/esm/AuditLog.d.ts.map +1 -1
- package/esm/AuditLog.js.map +1 -1
- package/esm/AuditLog.mjs +180 -66
- package/esm/Authenticator.d.ts.map +1 -1
- package/esm/Authenticator.js.map +1 -1
- package/esm/Authenticator.mjs +180 -66
- package/esm/Configuration.d.ts.map +1 -1
- package/esm/Configuration.js.map +1 -1
- package/esm/Configuration.mjs +180 -66
- package/esm/ConflictEvent.d.ts.map +1 -1
- package/esm/ConflictEvent.js.map +1 -1
- package/esm/ConflictEvent.mjs +180 -66
- package/esm/ConnectionHealthSnapshot.d.ts.map +1 -1
- package/esm/ConnectionHealthSnapshot.js.map +1 -1
- package/esm/ConnectionHealthSnapshot.mjs +180 -66
- package/esm/Customer.d.ts.map +1 -1
- package/esm/Customer.js.map +1 -1
- package/esm/Customer.mjs +180 -66
- package/esm/DeadLetterMessage.d.ts.map +1 -1
- package/esm/DeadLetterMessage.js.map +1 -1
- package/esm/DeadLetterMessage.mjs +180 -66
- package/esm/DecisionMemorySummary.d.ts.map +1 -1
- package/esm/DecisionMemorySummary.js.map +1 -1
- package/esm/DecisionMemorySummary.mjs +180 -66
- package/esm/EconomicEvent.d.ts.map +1 -1
- package/esm/EconomicEvent.js.map +1 -1
- package/esm/EconomicEvent.mjs +180 -66
- package/esm/EquityBar.d.ts.map +1 -1
- package/esm/EquityBar.js.map +1 -1
- package/esm/EquityBar.mjs +180 -66
- package/esm/Event.d.ts.map +1 -1
- package/esm/Event.js.map +1 -1
- package/esm/Event.mjs +180 -66
- package/esm/EventSnapshot.d.ts.map +1 -1
- package/esm/EventSnapshot.js.map +1 -1
- package/esm/EventSnapshot.mjs +180 -66
- package/esm/FeatureImportanceAnalysis.d.ts.map +1 -1
- package/esm/FeatureImportanceAnalysis.js.map +1 -1
- package/esm/FeatureImportanceAnalysis.mjs +180 -66
- package/esm/InstitutionalFlowSignal.d.ts.map +1 -1
- package/esm/InstitutionalFlowSignal.js.map +1 -1
- package/esm/InstitutionalFlowSignal.mjs +180 -66
- package/esm/InstitutionalHolding.d.ts.map +1 -1
- package/esm/InstitutionalHolding.js.map +1 -1
- package/esm/InstitutionalHolding.mjs +180 -66
- package/esm/InstitutionalSentimentAlerts.d.ts.map +1 -1
- package/esm/InstitutionalSentimentAlerts.js.map +1 -1
- package/esm/InstitutionalSentimentAlerts.mjs +180 -66
- package/esm/InstitutionalSentimentErrors.d.ts.map +1 -1
- package/esm/InstitutionalSentimentErrors.js.map +1 -1
- package/esm/InstitutionalSentimentErrors.mjs +180 -66
- package/esm/InstitutionalSentimentHistory.d.ts.map +1 -1
- package/esm/InstitutionalSentimentHistory.js.map +1 -1
- package/esm/InstitutionalSentimentHistory.mjs +180 -66
- package/esm/InstitutionalSentimentMetrics.d.ts.map +1 -1
- package/esm/InstitutionalSentimentMetrics.js.map +1 -1
- package/esm/InstitutionalSentimentMetrics.mjs +180 -66
- package/esm/InviteToken.d.ts.map +1 -1
- package/esm/InviteToken.js.map +1 -1
- package/esm/InviteToken.mjs +180 -66
- package/esm/LinkedProvider.d.ts.map +1 -1
- package/esm/LinkedProvider.js.map +1 -1
- package/esm/LinkedProvider.mjs +180 -66
- package/esm/LlmConfiguration.d.ts.map +1 -1
- package/esm/LlmConfiguration.js.map +1 -1
- package/esm/LlmConfiguration.mjs +180 -66
- package/esm/MLModelVersion.d.ts.map +1 -1
- package/esm/MLModelVersion.js.map +1 -1
- package/esm/MLModelVersion.mjs +180 -66
- package/esm/MLTrainingData.d.ts.map +1 -1
- package/esm/MLTrainingData.js.map +1 -1
- package/esm/MLTrainingData.mjs +180 -66
- package/esm/MarketSentiment.d.ts.map +1 -1
- package/esm/MarketSentiment.js.map +1 -1
- package/esm/MarketSentiment.mjs +180 -66
- package/esm/ModelArtifact.d.ts.map +1 -1
- package/esm/ModelArtifact.js.map +1 -1
- package/esm/ModelArtifact.mjs +180 -66
- package/esm/ModelVersion.d.ts.map +1 -1
- package/esm/ModelVersion.js.map +1 -1
- package/esm/ModelVersion.mjs +180 -66
- package/esm/ModelVersionArtifact.d.ts.map +1 -1
- package/esm/ModelVersionArtifact.js.map +1 -1
- package/esm/ModelVersionArtifact.mjs +180 -66
- package/esm/NewsArticle.d.ts.map +1 -1
- package/esm/NewsArticle.js.map +1 -1
- package/esm/NewsArticle.mjs +180 -66
- package/esm/NewsArticleAssetSentiment.d.ts.map +1 -1
- package/esm/NewsArticleAssetSentiment.js.map +1 -1
- package/esm/NewsArticleAssetSentiment.mjs +180 -66
- package/esm/OptionsContract.d.ts.map +1 -1
- package/esm/OptionsContract.js.map +1 -1
- package/esm/OptionsContract.mjs +180 -66
- package/esm/OptionsGreeksHistory.d.ts.map +1 -1
- package/esm/OptionsGreeksHistory.js.map +1 -1
- package/esm/OptionsGreeksHistory.mjs +180 -66
- package/esm/OptionsPosition.d.ts.map +1 -1
- package/esm/OptionsPosition.js.map +1 -1
- package/esm/OptionsPosition.mjs +180 -66
- package/esm/OptionsPositionEvent.d.ts.map +1 -1
- package/esm/OptionsPositionEvent.js.map +1 -1
- package/esm/OptionsPositionEvent.mjs +180 -66
- package/esm/OptionsTradeExecution.d.ts.map +1 -1
- package/esm/OptionsTradeExecution.js.map +1 -1
- package/esm/OptionsTradeExecution.mjs +180 -66
- package/esm/PolicyOverlay.d.ts.map +1 -1
- package/esm/PolicyOverlay.js.map +1 -1
- package/esm/PolicyOverlay.mjs +180 -66
- package/esm/PortfolioGreeksHistory.d.ts.map +1 -1
- package/esm/PortfolioGreeksHistory.js.map +1 -1
- package/esm/PortfolioGreeksHistory.mjs +180 -66
- package/esm/ScheduledOptionOrder.d.ts.map +1 -1
- package/esm/ScheduledOptionOrder.js.map +1 -1
- package/esm/ScheduledOptionOrder.mjs +180 -66
- package/esm/Session.d.ts.map +1 -1
- package/esm/Session.js.map +1 -1
- package/esm/Session.mjs +180 -66
- package/esm/SignalGeneratorMetrics.d.ts.map +1 -1
- package/esm/SignalGeneratorMetrics.js.map +1 -1
- package/esm/SignalGeneratorMetrics.mjs +180 -66
- package/esm/SignalLineage.d.ts.map +1 -1
- package/esm/SignalLineage.js.map +1 -1
- package/esm/SignalLineage.mjs +180 -66
- package/esm/SignalOutcome.d.ts.map +1 -1
- package/esm/SignalOutcome.js.map +1 -1
- package/esm/SignalOutcome.mjs +180 -66
- package/esm/SignalPriorityQueue.d.ts.map +1 -1
- package/esm/SignalPriorityQueue.js.map +1 -1
- package/esm/SignalPriorityQueue.mjs +180 -66
- package/esm/SyncEvent.d.ts.map +1 -1
- package/esm/SyncEvent.js.map +1 -1
- package/esm/SyncEvent.mjs +180 -66
- package/esm/SystemAlert.d.ts.map +1 -1
- package/esm/SystemAlert.js.map +1 -1
- package/esm/SystemAlert.mjs +180 -66
- package/esm/Trade.d.ts.map +1 -1
- package/esm/Trade.js.map +1 -1
- package/esm/Trade.mjs +180 -66
- package/esm/TradeAuditEvent.d.ts.map +1 -1
- package/esm/TradeAuditEvent.js.map +1 -1
- package/esm/TradeAuditEvent.mjs +180 -66
- package/esm/TradeExecutionHistory.d.ts.map +1 -1
- package/esm/TradeExecutionHistory.js.map +1 -1
- package/esm/TradeExecutionHistory.mjs +180 -66
- package/esm/TradeOutcome.d.ts.map +1 -1
- package/esm/TradeOutcome.js.map +1 -1
- package/esm/TradeOutcome.mjs +180 -66
- package/esm/TradingPolicy.d.ts.map +1 -1
- package/esm/TradingPolicy.js.map +1 -1
- package/esm/TradingPolicy.mjs +180 -66
- package/esm/User.d.ts.map +1 -1
- package/esm/User.js.map +1 -1
- package/esm/User.mjs +180 -66
- package/esm/VerificationToken.d.ts.map +1 -1
- package/esm/VerificationToken.js.map +1 -1
- package/esm/VerificationToken.mjs +180 -66
- package/esm/WaitlistEntry.d.ts.map +1 -1
- package/esm/WaitlistEntry.js.map +1 -1
- package/esm/WaitlistEntry.mjs +180 -66
- package/esm/client.d.ts +32 -0
- package/esm/client.d.ts.map +1 -1
- package/esm/client.js.map +1 -1
- package/esm/client.mjs +130 -9
- package/esm/index.d.ts +2 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js.map +1 -1
- package/esm/index.mjs +1 -1
- package/index.cjs +2 -1
- package/index.d.ts +2 -2
- package/package.json +1 -1
package/client.cjs
CHANGED
|
@@ -39,7 +39,9 @@ exports.configureConnectionPool = configureConnectionPool;
|
|
|
39
39
|
exports.setTokenProvider = setTokenProvider;
|
|
40
40
|
exports.getApolloClient = getApolloClient;
|
|
41
41
|
exports.getApolloModules = getApolloModules;
|
|
42
|
+
exports.getPoolStats = getPoolStats;
|
|
42
43
|
exports.stopClient = stopClient;
|
|
44
|
+
const node_crypto_1 = require("node:crypto");
|
|
43
45
|
const logger_1 = require("./utils/logger");
|
|
44
46
|
const DEFAULT_POOL_CONFIG = {
|
|
45
47
|
maxConcurrentOperations: 100, // Maximum concurrent operations to the database
|
|
@@ -56,6 +58,18 @@ let pendingOperations = 0;
|
|
|
56
58
|
const operationQueue = [];
|
|
57
59
|
let poolConfig = DEFAULT_POOL_CONFIG;
|
|
58
60
|
let customTokenProvider;
|
|
61
|
+
// === Pool observability counters ===
|
|
62
|
+
// These are best-effort process-local counters. They are exposed via
|
|
63
|
+
// `getPoolStats()` for engine-side Prometheus scraping. Resetting
|
|
64
|
+
// categorical counters is not offered — callers that care about deltas
|
|
65
|
+
// should sample-then-diff.
|
|
66
|
+
const poolCounters = {
|
|
67
|
+
executedTotal: 0,
|
|
68
|
+
rejectedLoadShed: 0,
|
|
69
|
+
rejectedQueueWaitTimeout: 0,
|
|
70
|
+
rejectedNonRetryable: 0,
|
|
71
|
+
retryAttemptsTotal: 0,
|
|
72
|
+
};
|
|
59
73
|
/**
|
|
60
74
|
* Dynamically loads the correct Apollo modules based on the runtime environment.
|
|
61
75
|
*/
|
|
@@ -154,15 +168,23 @@ async function getAuthToken() {
|
|
|
154
168
|
}
|
|
155
169
|
/**
|
|
156
170
|
* Processes the operation queue respecting the connection pool limits.
|
|
171
|
+
*
|
|
172
|
+
* Before executing each dequeued operation, this also performs a proactive
|
|
173
|
+
* queue-wait-timeout sweep: any operation that has been waiting longer than
|
|
174
|
+
* `queueWaitTimeoutMs` on its first attempt is rejected inline by its own
|
|
175
|
+
* `executeWithRetry` (attempt === 0 branch) rather than consuming a slot.
|
|
176
|
+
* The sweep runs here (not on a separate timer) because queue pressure
|
|
177
|
+
* almost always clears at the same moment a slot opens up, so the check
|
|
178
|
+
* is cheap and avoids introducing another timer.
|
|
157
179
|
*/
|
|
158
180
|
function processQueue() {
|
|
159
181
|
// Process queue until we reach max concurrent operations or queue is empty
|
|
160
182
|
while (pendingOperations < poolConfig.maxConcurrentOperations &&
|
|
161
183
|
operationQueue.length > 0) {
|
|
162
|
-
const
|
|
163
|
-
if (
|
|
184
|
+
const entry = operationQueue.shift();
|
|
185
|
+
if (entry) {
|
|
164
186
|
pendingOperations++;
|
|
165
|
-
void
|
|
187
|
+
void entry.executeWithRetry().finally(() => {
|
|
166
188
|
pendingOperations--;
|
|
167
189
|
processQueue(); // Process next item after an operation completes
|
|
168
190
|
});
|
|
@@ -206,6 +228,50 @@ function extractOperationName(options) {
|
|
|
206
228
|
* timeouts are indistinguishable in logs and root-cause diagnosis is
|
|
207
229
|
* impossible.
|
|
208
230
|
*/
|
|
231
|
+
/**
|
|
232
|
+
* Classify an error as definitively non-retryable. These errors cost capacity
|
|
233
|
+
* when retried and cannot succeed without the caller changing behaviour:
|
|
234
|
+
*
|
|
235
|
+
* - **Accelerate 1102 "Worker exceeded resource limits"**: the Cloudflare
|
|
236
|
+
* Worker backing Prisma Accelerate hit its CPU/memory budget. Cloudflare's
|
|
237
|
+
* guidance is explicit: do not retry — retrying the same shape will hit
|
|
238
|
+
* the same limit and only inflates worker concurrency across the fleet,
|
|
239
|
+
* which can trigger broader throttling. Fix is at the caller (smaller
|
|
240
|
+
* batch, narrower selection, chunked write).
|
|
241
|
+
* - **P2002 unique-constraint violation**: another writer already inserted
|
|
242
|
+
* the conflicting row. Retrying the same mutation produces the same
|
|
243
|
+
* failure. Fix is at the caller (idempotent find-or-create, stable
|
|
244
|
+
* idempotency key, serialized writer).
|
|
245
|
+
* - **P2025 "record not found" race**: a concurrent delete/update removed
|
|
246
|
+
* the target row. Retrying a delete/update against a vanished row loops.
|
|
247
|
+
* - **GraphQL schema validation / 400 Bad Request**: the query itself is
|
|
248
|
+
* malformed; retrying guarantees the same 400.
|
|
249
|
+
*
|
|
250
|
+
* Explicit classification avoids the previous pattern where the generic
|
|
251
|
+
* `error.message.includes('Accelerate')` branch treated all Accelerate
|
|
252
|
+
* errors — including 1102 — as retryable, turning a single worker-limit
|
|
253
|
+
* breach into a retry storm that further starved the Accelerate worker
|
|
254
|
+
* pool. See Cloudflare error codes
|
|
255
|
+
* (https://developers.cloudflare.com/workers/observability/errors/#error-1102).
|
|
256
|
+
*/
|
|
257
|
+
function isDefinitivelyNonRetryable(message) {
|
|
258
|
+
return (
|
|
259
|
+
// Accelerate Worker resource-limit errors — never retry
|
|
260
|
+
message.includes('1102') ||
|
|
261
|
+
message.includes('Worker exceeded resource limits') ||
|
|
262
|
+
message.includes('Worker exceeded CPU') ||
|
|
263
|
+
message.includes('Worker exceeded memory') ||
|
|
264
|
+
// Prisma unique-constraint violation — retry just reproduces it
|
|
265
|
+
message.includes('P2002') ||
|
|
266
|
+
message.includes('Unique constraint failed') ||
|
|
267
|
+
// Prisma record-not-found race — deletes/updates already lost the row
|
|
268
|
+
message.includes('P2025') ||
|
|
269
|
+
message.includes('Record to update not found') ||
|
|
270
|
+
message.includes('Record to delete does not exist') ||
|
|
271
|
+
// GraphQL schema / 400 — malformed query, retry is pointless
|
|
272
|
+
message.includes('GRAPHQL_VALIDATION_FAILED') ||
|
|
273
|
+
message.includes('status code 400'));
|
|
274
|
+
}
|
|
209
275
|
async function enqueueOperation(operation, operationName, attempt = 0) {
|
|
210
276
|
// Load shedding: reject new (first-attempt) operations when the queue is
|
|
211
277
|
// saturated. This turns a cascading 30s timeout storm during backend
|
|
@@ -213,8 +279,8 @@ async function enqueueOperation(operation, operationName, attempt = 0) {
|
|
|
213
279
|
// retry later, or skip this cycle). Retries bypass this check because
|
|
214
280
|
// they represent in-flight commitments that already consumed backend
|
|
215
281
|
// capacity on the first attempt.
|
|
216
|
-
if (attempt === 0 &&
|
|
217
|
-
|
|
282
|
+
if (attempt === 0 && operationQueue.length >= poolConfig.maxQueueDepth) {
|
|
283
|
+
poolCounters.rejectedLoadShed++;
|
|
218
284
|
logger_1.logger.warn(`Apollo operation '${operationName}' rejected — queue saturated (${operationQueue.length}/${poolConfig.maxQueueDepth})`, {
|
|
219
285
|
operation: operationName,
|
|
220
286
|
queueDepth: operationQueue.length,
|
|
@@ -237,6 +303,7 @@ async function enqueueOperation(operation, operationName, attempt = 0) {
|
|
|
237
303
|
if (attempt === 0) {
|
|
238
304
|
const waitMs = Date.now() - enqueuedAt;
|
|
239
305
|
if (waitMs > poolConfig.queueWaitTimeoutMs) {
|
|
306
|
+
poolCounters.rejectedQueueWaitTimeout++;
|
|
240
307
|
logger_1.logger.warn(`Apollo operation '${operationName}' dropped — queue wait exceeded ${poolConfig.queueWaitTimeoutMs}ms (waited ${waitMs}ms)`, {
|
|
241
308
|
operation: operationName,
|
|
242
309
|
waitMs,
|
|
@@ -251,6 +318,7 @@ async function enqueueOperation(operation, operationName, attempt = 0) {
|
|
|
251
318
|
}
|
|
252
319
|
try {
|
|
253
320
|
const result = await operation();
|
|
321
|
+
poolCounters.executedTotal++;
|
|
254
322
|
resolve(result);
|
|
255
323
|
}
|
|
256
324
|
catch (error) {
|
|
@@ -276,10 +344,22 @@ async function enqueueOperation(operation, operationName, attempt = 0) {
|
|
|
276
344
|
// engine's database-operations-wrapper already handles this path with
|
|
277
345
|
// idempotent=true).
|
|
278
346
|
const isNonRetryableDelete = operationName.startsWith('deleteOne') && isTimeoutLike;
|
|
347
|
+
// Hard non-retry classes: retrying guarantees the same failure AND
|
|
348
|
+
// wastes backend capacity. Cross-check this list against caller-side
|
|
349
|
+
// handling — engine's graphql-error-handler should also treat these
|
|
350
|
+
// as non-retryable so double-wrapped callers don't re-retry.
|
|
351
|
+
const isHardNonRetryable = isDefinitivelyNonRetryable(errorMessage);
|
|
279
352
|
const isRetryable = !isNonRetryableDelete &&
|
|
353
|
+
!isHardNonRetryable &&
|
|
280
354
|
error instanceof Error &&
|
|
281
|
-
|
|
282
|
-
|
|
355
|
+
// Narrowed from the previous broad `includes('Accelerate')`: that
|
|
356
|
+
// matched 1102 (Worker resource-limit) and fed retry storms. Keep
|
|
357
|
+
// only transient Accelerate classes that genuinely benefit from
|
|
358
|
+
// retry (1016 = origin DNS temporarily unresolved, 1033 = worker
|
|
359
|
+
// script temporarily unreachable).
|
|
360
|
+
(error.message.includes('code: 1016') ||
|
|
361
|
+
error.message.includes('code: 1033') ||
|
|
362
|
+
error.message.includes('Accelerate transient') ||
|
|
283
363
|
error.message.includes('ECONNREFUSED') ||
|
|
284
364
|
error.message.includes('ECONNRESET') ||
|
|
285
365
|
error.message.includes('ETIMEDOUT') ||
|
|
@@ -304,14 +384,36 @@ async function enqueueOperation(operation, operationName, attempt = 0) {
|
|
|
304
384
|
category: 'SUPPRESSED_DELETE_RETRY',
|
|
305
385
|
});
|
|
306
386
|
}
|
|
387
|
+
if (isHardNonRetryable) {
|
|
388
|
+
poolCounters.rejectedNonRetryable++;
|
|
389
|
+
logger_1.logger.warn(`Apollo operation '${operationName}' failed with non-retryable error — NOT retrying (would reproduce same failure or hit resource limit)`, {
|
|
390
|
+
operation: operationName,
|
|
391
|
+
attempt: attempt + 1,
|
|
392
|
+
error: errorMessage,
|
|
393
|
+
category: 'NON_RETRYABLE_HARD',
|
|
394
|
+
});
|
|
395
|
+
}
|
|
307
396
|
if (attempt < poolConfig.retryAttempts && isRetryable) {
|
|
308
|
-
|
|
397
|
+
// Decorrelated jitter: delay = random_between(retryDelay, base * 3)
|
|
398
|
+
// where base = retryDelay * 2^attempt. This breaks the thundering
|
|
399
|
+
// herd pattern where every op enqueued at the same stall moment
|
|
400
|
+
// retries at the same time (1.5s, 3s, 6s — all in lockstep) and
|
|
401
|
+
// overwhelms the backend the instant it recovers. See AWS
|
|
402
|
+
// architecture blog "Exponential Backoff And Jitter".
|
|
403
|
+
const base = poolConfig.retryDelay * Math.pow(2, attempt);
|
|
404
|
+
const upper = base * 3;
|
|
405
|
+
const lower = poolConfig.retryDelay;
|
|
406
|
+
const delay = lower >= upper
|
|
407
|
+
? upper
|
|
408
|
+
: (0, node_crypto_1.randomInt)(Math.floor(lower), Math.floor(upper) + 1);
|
|
409
|
+
poolCounters.retryAttemptsTotal++;
|
|
309
410
|
logger_1.logger.warn(`Apollo operation '${operationName}' failed, retrying in ${delay}ms (attempt ${attempt + 1}/${poolConfig.retryAttempts})`, {
|
|
310
411
|
operation: operationName,
|
|
311
412
|
attempt: attempt + 1,
|
|
312
413
|
maxAttempts: poolConfig.retryAttempts,
|
|
313
414
|
delayMs: delay,
|
|
314
415
|
error: errorMessage,
|
|
416
|
+
category: 'RETRY',
|
|
315
417
|
});
|
|
316
418
|
setTimeout(() => {
|
|
317
419
|
enqueueOperation(operation, operationName, attempt + 1)
|
|
@@ -325,13 +427,14 @@ async function enqueueOperation(operation, operationName, attempt = 0) {
|
|
|
325
427
|
operation: operationName,
|
|
326
428
|
attempts: poolConfig.retryAttempts,
|
|
327
429
|
error: errorMessage,
|
|
430
|
+
category: 'RETRY_EXHAUSTED',
|
|
328
431
|
});
|
|
329
432
|
}
|
|
330
433
|
reject(error);
|
|
331
434
|
}
|
|
332
435
|
}
|
|
333
436
|
};
|
|
334
|
-
operationQueue.push(executeWithRetry);
|
|
437
|
+
operationQueue.push({ executeWithRetry, enqueuedAt, operationName });
|
|
335
438
|
processQueue();
|
|
336
439
|
});
|
|
337
440
|
}
|
|
@@ -538,6 +641,25 @@ exports.client = {
|
|
|
538
641
|
return getApolloClient().then(onfulfilled, onrejected);
|
|
539
642
|
},
|
|
540
643
|
};
|
|
644
|
+
/**
|
|
645
|
+
* Returns a read-only snapshot of pool state for observability.
|
|
646
|
+
* Safe to call on every scrape interval (O(1)).
|
|
647
|
+
*/
|
|
648
|
+
function getPoolStats() {
|
|
649
|
+
return {
|
|
650
|
+
pendingOperations,
|
|
651
|
+
queueDepth: operationQueue.length,
|
|
652
|
+
maxConcurrentOperations: poolConfig.maxConcurrentOperations,
|
|
653
|
+
maxQueueDepth: poolConfig.maxQueueDepth,
|
|
654
|
+
queueWaitTimeoutMs: poolConfig.queueWaitTimeoutMs,
|
|
655
|
+
connectionTimeoutMs: poolConfig.connectionTimeout,
|
|
656
|
+
executedTotal: poolCounters.executedTotal,
|
|
657
|
+
rejectedLoadShed: poolCounters.rejectedLoadShed,
|
|
658
|
+
rejectedQueueWaitTimeout: poolCounters.rejectedQueueWaitTimeout,
|
|
659
|
+
rejectedNonRetryable: poolCounters.rejectedNonRetryable,
|
|
660
|
+
retryAttemptsTotal: poolCounters.retryAttemptsTotal,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
541
663
|
/**
|
|
542
664
|
* Gracefully stops the singleton Apollo Client and releases its resources.
|
|
543
665
|
* Call this during process shutdown to close keep-alive HTTP connections and
|
package/client.d.ts
CHANGED
|
@@ -100,6 +100,38 @@ export declare function getApolloModules(): Promise<ApolloModules>;
|
|
|
100
100
|
* @deprecated Prefer calling `getApolloClient()` directly for explicit lifecycle control.
|
|
101
101
|
*/
|
|
102
102
|
export declare const client: PromiseLike<ApolloClientType<NormalizedCacheObject>>;
|
|
103
|
+
/**
|
|
104
|
+
* Snapshot of connection-pool state at the instant `getPoolStats` is called.
|
|
105
|
+
* Exposed so downstream observability (engine's Prometheus registry) can
|
|
106
|
+
* scrape pool depth, pending-op concurrency, retry counters, and rejection
|
|
107
|
+
* categories without having to reach into module-private state.
|
|
108
|
+
*/
|
|
109
|
+
export interface PoolStats {
|
|
110
|
+
/** Operations currently in flight (post-dequeue, pre-resolution). */
|
|
111
|
+
pendingOperations: number;
|
|
112
|
+
/** Operations waiting in the queue for a concurrency slot. */
|
|
113
|
+
queueDepth: number;
|
|
114
|
+
/** Configured limits at the time of sampling. */
|
|
115
|
+
maxConcurrentOperations: number;
|
|
116
|
+
maxQueueDepth: number;
|
|
117
|
+
queueWaitTimeoutMs: number;
|
|
118
|
+
connectionTimeoutMs: number;
|
|
119
|
+
/** Count of operations whose underlying fetch resolved (success path). */
|
|
120
|
+
executedTotal: number;
|
|
121
|
+
/** Count of first-attempt ops rejected by load-shedding admission control. */
|
|
122
|
+
rejectedLoadShed: number;
|
|
123
|
+
/** Count of ops rejected after queue-wait-timeout at dequeue. */
|
|
124
|
+
rejectedQueueWaitTimeout: number;
|
|
125
|
+
/** Count of ops that failed with a hard non-retryable error class. */
|
|
126
|
+
rejectedNonRetryable: number;
|
|
127
|
+
/** Count of retry attempts fired across all operations. */
|
|
128
|
+
retryAttemptsTotal: number;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Returns a read-only snapshot of pool state for observability.
|
|
132
|
+
* Safe to call on every scrape interval (O(1)).
|
|
133
|
+
*/
|
|
134
|
+
export declare function getPoolStats(): PoolStats;
|
|
103
135
|
/**
|
|
104
136
|
* Gracefully stops the singleton Apollo Client and releases its resources.
|
|
105
137
|
* Call this during process shutdown to close keep-alive HTTP connections and
|
package/esm/ABTest.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ABTest.d.ts","sourceRoot":"","sources":["../../src/ABTest.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,8CAA8C,CAAC;AACpF,OAAO,EAAmB,gBAAgB,EAAE,qBAAqB,EAAoB,MAAM,UAAU,CAAC;AAsLpG,eAAO,MAAM,MAAM;IAEjB;;;;;OAKG;IAEH;;;;;;OAMG;kBACiB,UAAU,iBAAiB,gBAAgB,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"ABTest.d.ts","sourceRoot":"","sources":["../../src/ABTest.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,8CAA8C,CAAC;AACpF,OAAO,EAAmB,gBAAgB,EAAE,qBAAqB,EAAoB,MAAM,UAAU,CAAC;AAsLpG,eAAO,MAAM,MAAM;IAEjB;;;;;OAKG;IAEH;;;;;;OAMG;kBACiB,UAAU,iBAAiB,gBAAgB,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAgmD9G;;;;;;;OAOG;sBACqB,UAAU,EAAE,iBAAiB,gBAAgB,CAAC,qBAAqB,CAAC,YAAY;QAAE,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAuKxK;;;;;;OAMG;kBACiB,UAAU,iBAAiB,gBAAgB,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAq4L5G;;;;;;OAMG;kBACiB,UAAU,iBAAiB,gBAAgB,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAw1O5G;;;;;;OAMG;sBACqB,UAAU,EAAE,iBAAiB,gBAAgB,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAm4LhI;;;;;;OAMG;kBACiB,UAAU,iBAAiB,gBAAgB,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAqJ5G;;;;;;;OAOG;eACc,UAAU,iBAAiB,gBAAgB,CAAC,qBAAqB,CAAC,eAAe,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAiItJ;;;;;OAKG;0BACyB,gBAAgB,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC;IA6GlG;;;;;;;OAOG;oBACmB,UAAU,iBAAiB,gBAAgB,CAAC,qBAAqB,CAAC,eAAe,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC;CA6I9J,CAAC"}
|