@utaba/deep-memory-storage-cosmosdb 0.12.0 → 0.14.0

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/dist/index.cjs CHANGED
@@ -1127,41 +1127,71 @@ function isInTimeRange(timestamp, timeRange) {
1127
1127
 
1128
1128
  // src/queries/adaptive-import.ts
1129
1129
  var import_deep_memory5 = require("@utaba/deep-memory");
1130
- var DEFAULT_MIN = 2;
1130
+ var DEFAULT_MIN = 1;
1131
1131
  var DEFAULT_START = 5;
1132
1132
  var DEFAULT_MAX = 32;
1133
1133
  var DEFAULT_INCREASE_AFTER = 50;
1134
1134
  var DEFAULT_COOLDOWN_MS = 1e3;
1135
+ var DEFAULT_RAMP_UP_COOLDOWN_MS = 5e3;
1135
1136
  var DEFAULT_MAX_CONSECUTIVE_THROTTLES_AT_MIN = 10;
1137
+ var DEFAULT_THROTTLE_CEILING_MULTIPLIER = 3;
1138
+ var NO_SOFT_CEILING = Number.POSITIVE_INFINITY;
1136
1139
  function resolveOptions(opts) {
1137
1140
  const min = Math.max(1, opts?.min ?? DEFAULT_MIN);
1138
1141
  const max = Math.max(min, opts?.max ?? DEFAULT_MAX);
1139
1142
  const start = Math.min(max, Math.max(min, opts?.start ?? DEFAULT_START));
1140
1143
  const increaseAfter = Math.max(1, opts?.increaseAfter ?? DEFAULT_INCREASE_AFTER);
1141
1144
  const cooldownMs = Math.max(0, opts?.cooldownMs ?? DEFAULT_COOLDOWN_MS);
1145
+ const rampUpCooldownMs = Math.max(0, opts?.rampUpCooldownMs ?? DEFAULT_RAMP_UP_COOLDOWN_MS);
1142
1146
  const maxConsecutiveThrottlesAtMin = Math.max(
1143
1147
  1,
1144
1148
  opts?.maxConsecutiveThrottlesAtMin ?? DEFAULT_MAX_CONSECUTIVE_THROTTLES_AT_MIN
1145
1149
  );
1150
+ const throttleCeilingMultiplier = Math.max(
1151
+ 1,
1152
+ opts?.throttleCeilingMultiplier ?? DEFAULT_THROTTLE_CEILING_MULTIPLIER
1153
+ );
1146
1154
  return {
1147
1155
  min,
1148
1156
  start,
1149
1157
  max,
1150
1158
  increaseAfter,
1151
1159
  cooldownMs,
1160
+ rampUpCooldownMs,
1152
1161
  maxConsecutiveThrottlesAtMin,
1162
+ throttleCeilingMultiplier,
1153
1163
  onAdjust: opts?.onAdjust
1154
1164
  };
1155
1165
  }
1166
+ function resolveController(options, handle) {
1167
+ if (!handle) {
1168
+ return new AdaptiveConcurrencyController(options);
1169
+ }
1170
+ const cosmosHandle = handle;
1171
+ if (!cosmosHandle.controller) {
1172
+ cosmosHandle.controller = new AdaptiveConcurrencyController(options);
1173
+ }
1174
+ return cosmosHandle.controller;
1175
+ }
1156
1176
  var AdaptiveConcurrencyController = class {
1157
1177
  opts;
1158
1178
  current;
1159
1179
  streak = 0;
1160
1180
  cooldownUntil = 0;
1181
+ rampFrozenUntil = 0;
1161
1182
  completed = 0;
1162
1183
  throttled = 0;
1163
1184
  startEmitted = false;
1164
1185
  consecutiveThrottlesAtMin = 0;
1186
+ /**
1187
+ * Highest concurrency at which a throttle has been observed and the
1188
+ * controller actually halved (i.e. concrete evidence the cluster could not
1189
+ * sustain that level). Re-approaching this level on subsequent ramp-ups
1190
+ * requires `increaseAfter * throttleCeilingMultiplier` consecutive successes
1191
+ * instead of just `increaseAfter`. Cleared once we've successfully held the
1192
+ * level without further throttling — see noteSuccess.
1193
+ */
1194
+ softCeiling = NO_SOFT_CEILING;
1165
1195
  constructor(opts) {
1166
1196
  this.opts = resolveOptions(opts);
1167
1197
  this.current = this.opts.start;
@@ -1199,14 +1229,21 @@ var AdaptiveConcurrencyController = class {
1199
1229
  this.notify("start", this.current);
1200
1230
  }
1201
1231
  /** Record a throttle-free task completion. May trigger ramp-up. */
1202
- noteSuccess() {
1232
+ noteSuccess(now = Date.now()) {
1203
1233
  this.completed++;
1204
- this.streak++;
1205
1234
  this.consecutiveThrottlesAtMin = 0;
1206
- if (this.streak >= this.opts.increaseAfter && this.current < this.opts.max) {
1235
+ if (now < this.rampFrozenUntil) return;
1236
+ this.streak++;
1237
+ if (this.current >= this.opts.max) return;
1238
+ const target = this.current + 1;
1239
+ const needed = target >= this.softCeiling ? this.opts.increaseAfter * this.opts.throttleCeilingMultiplier : this.opts.increaseAfter;
1240
+ if (this.streak >= needed) {
1207
1241
  const previous = this.current;
1208
- this.current = Math.min(this.opts.max, this.current + 1);
1242
+ this.current = Math.min(this.opts.max, target);
1209
1243
  this.streak = 0;
1244
+ if (this.current >= this.softCeiling) {
1245
+ this.softCeiling = NO_SOFT_CEILING;
1246
+ }
1210
1247
  this.notify("ramp-up", previous);
1211
1248
  }
1212
1249
  }
@@ -1218,8 +1255,10 @@ var AdaptiveConcurrencyController = class {
1218
1255
  const previous = this.current;
1219
1256
  const next = Math.max(this.opts.min, Math.floor(this.current / 2));
1220
1257
  this.cooldownUntil = now + this.opts.cooldownMs;
1258
+ this.rampFrozenUntil = now + this.opts.rampUpCooldownMs;
1221
1259
  if (next !== previous) {
1222
1260
  this.current = next;
1261
+ this.softCeiling = previous;
1223
1262
  this.notify("throttle", previous);
1224
1263
  }
1225
1264
  if (this.current === this.opts.min) {
@@ -1228,6 +1267,15 @@ var AdaptiveConcurrencyController = class {
1228
1267
  this.consecutiveThrottlesAtMin = 0;
1229
1268
  }
1230
1269
  }
1270
+ /**
1271
+ * Current soft ceiling — the highest concurrency at which a throttle was
1272
+ * observed and the controller halved. {@link Number.POSITIVE_INFINITY} when
1273
+ * no ceiling is in effect. Re-approaching this level requires
1274
+ * `increaseAfter * throttleCeilingMultiplier` consecutive successes.
1275
+ */
1276
+ getSoftCeiling() {
1277
+ return this.softCeiling;
1278
+ }
1231
1279
  /** Configured circuit-breaker threshold. */
1232
1280
  getMaxConsecutiveThrottlesAtMin() {
1233
1281
  return this.opts.maxConsecutiveThrottlesAtMin;
@@ -1408,7 +1456,7 @@ async function importBulk(conn, repositoryId, data, options) {
1408
1456
  let relationshipsImported = 0;
1409
1457
  const errors = [];
1410
1458
  const skipCheck = options?.skipExistenceCheck ?? false;
1411
- const controller = new AdaptiveConcurrencyController(options?.adaptiveConcurrency);
1459
+ const controller = resolveController(options?.adaptiveConcurrency, options?.adaptiveConcurrencyHandle);
1412
1460
  for (const chunk of data) {
1413
1461
  if (chunk.entities && chunk.entities.length > 0) {
1414
1462
  const results = await runAdaptive(
@@ -2173,35 +2221,9 @@ var CosmosDbProvider = class {
2173
2221
  return this.track("executeNativeQuery", void 0, () => this.executeNativeQueryImpl(query, params));
2174
2222
  }
2175
2223
  async executeNativeQueryImpl(query, params) {
2176
- const startTime = Date.now();
2177
2224
  try {
2178
2225
  const result = await this.conn.submit(query, params);
2179
- const executionTimeMs = Date.now() - startTime;
2180
- const entities = [];
2181
- for (const item of result.items) {
2182
- try {
2183
- const entity = entityFromGremlin(item);
2184
- if (entity.id) {
2185
- entities.push(entity);
2186
- }
2187
- } catch {
2188
- }
2189
- }
2190
- const queryMetadata = {
2191
- executionTimeMs,
2192
- resourceCost: result.requestCharge != null ? { units: "RU", value: result.requestCharge } : void 0,
2193
- compiledQuery: query,
2194
- compiledQueryLanguage: "gremlin",
2195
- appliedLimits: { maxResults: entities.length },
2196
- truncated: false
2197
- };
2198
- return {
2199
- entities,
2200
- total: entities.length,
2201
- returned: entities.length,
2202
- hasMore: false,
2203
- queryMetadata
2204
- };
2226
+ return result.items;
2205
2227
  } catch (err) {
2206
2228
  throw new import_deep_memory6.ProviderError(
2207
2229
  `Native Gremlin query failed: ${err instanceof Error ? err.message : String(err)}`,