@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.d.cts CHANGED
@@ -147,7 +147,7 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
147
147
  * For agent-facing graph queries use {@link traverse}, which enforces the
148
148
  * repositoryId partition predicate.
149
149
  */
150
- executeNativeQuery(_repositoryId: string, query: string, params?: Record<string, unknown>): Promise<TraversalResult>;
150
+ executeNativeQuery(_repositoryId: string, query: string, params?: Record<string, unknown>): Promise<unknown[]>;
151
151
  private executeNativeQueryImpl;
152
152
  }
153
153
 
package/dist/index.d.ts CHANGED
@@ -147,7 +147,7 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
147
147
  * For agent-facing graph queries use {@link traverse}, which enforces the
148
148
  * repositoryId partition predicate.
149
149
  */
150
- executeNativeQuery(_repositoryId: string, query: string, params?: Record<string, unknown>): Promise<TraversalResult>;
150
+ executeNativeQuery(_repositoryId: string, query: string, params?: Record<string, unknown>): Promise<unknown[]>;
151
151
  private executeNativeQueryImpl;
152
152
  }
153
153
 
package/dist/index.js CHANGED
@@ -1090,41 +1090,71 @@ function isInTimeRange(timestamp, timeRange) {
1090
1090
 
1091
1091
  // src/queries/adaptive-import.ts
1092
1092
  import { ImportThrottleAbortError } from "@utaba/deep-memory";
1093
- var DEFAULT_MIN = 2;
1093
+ var DEFAULT_MIN = 1;
1094
1094
  var DEFAULT_START = 5;
1095
1095
  var DEFAULT_MAX = 32;
1096
1096
  var DEFAULT_INCREASE_AFTER = 50;
1097
1097
  var DEFAULT_COOLDOWN_MS = 1e3;
1098
+ var DEFAULT_RAMP_UP_COOLDOWN_MS = 5e3;
1098
1099
  var DEFAULT_MAX_CONSECUTIVE_THROTTLES_AT_MIN = 10;
1100
+ var DEFAULT_THROTTLE_CEILING_MULTIPLIER = 3;
1101
+ var NO_SOFT_CEILING = Number.POSITIVE_INFINITY;
1099
1102
  function resolveOptions(opts) {
1100
1103
  const min = Math.max(1, opts?.min ?? DEFAULT_MIN);
1101
1104
  const max = Math.max(min, opts?.max ?? DEFAULT_MAX);
1102
1105
  const start = Math.min(max, Math.max(min, opts?.start ?? DEFAULT_START));
1103
1106
  const increaseAfter = Math.max(1, opts?.increaseAfter ?? DEFAULT_INCREASE_AFTER);
1104
1107
  const cooldownMs = Math.max(0, opts?.cooldownMs ?? DEFAULT_COOLDOWN_MS);
1108
+ const rampUpCooldownMs = Math.max(0, opts?.rampUpCooldownMs ?? DEFAULT_RAMP_UP_COOLDOWN_MS);
1105
1109
  const maxConsecutiveThrottlesAtMin = Math.max(
1106
1110
  1,
1107
1111
  opts?.maxConsecutiveThrottlesAtMin ?? DEFAULT_MAX_CONSECUTIVE_THROTTLES_AT_MIN
1108
1112
  );
1113
+ const throttleCeilingMultiplier = Math.max(
1114
+ 1,
1115
+ opts?.throttleCeilingMultiplier ?? DEFAULT_THROTTLE_CEILING_MULTIPLIER
1116
+ );
1109
1117
  return {
1110
1118
  min,
1111
1119
  start,
1112
1120
  max,
1113
1121
  increaseAfter,
1114
1122
  cooldownMs,
1123
+ rampUpCooldownMs,
1115
1124
  maxConsecutiveThrottlesAtMin,
1125
+ throttleCeilingMultiplier,
1116
1126
  onAdjust: opts?.onAdjust
1117
1127
  };
1118
1128
  }
1129
+ function resolveController(options, handle) {
1130
+ if (!handle) {
1131
+ return new AdaptiveConcurrencyController(options);
1132
+ }
1133
+ const cosmosHandle = handle;
1134
+ if (!cosmosHandle.controller) {
1135
+ cosmosHandle.controller = new AdaptiveConcurrencyController(options);
1136
+ }
1137
+ return cosmosHandle.controller;
1138
+ }
1119
1139
  var AdaptiveConcurrencyController = class {
1120
1140
  opts;
1121
1141
  current;
1122
1142
  streak = 0;
1123
1143
  cooldownUntil = 0;
1144
+ rampFrozenUntil = 0;
1124
1145
  completed = 0;
1125
1146
  throttled = 0;
1126
1147
  startEmitted = false;
1127
1148
  consecutiveThrottlesAtMin = 0;
1149
+ /**
1150
+ * Highest concurrency at which a throttle has been observed and the
1151
+ * controller actually halved (i.e. concrete evidence the cluster could not
1152
+ * sustain that level). Re-approaching this level on subsequent ramp-ups
1153
+ * requires `increaseAfter * throttleCeilingMultiplier` consecutive successes
1154
+ * instead of just `increaseAfter`. Cleared once we've successfully held the
1155
+ * level without further throttling — see noteSuccess.
1156
+ */
1157
+ softCeiling = NO_SOFT_CEILING;
1128
1158
  constructor(opts) {
1129
1159
  this.opts = resolveOptions(opts);
1130
1160
  this.current = this.opts.start;
@@ -1162,14 +1192,21 @@ var AdaptiveConcurrencyController = class {
1162
1192
  this.notify("start", this.current);
1163
1193
  }
1164
1194
  /** Record a throttle-free task completion. May trigger ramp-up. */
1165
- noteSuccess() {
1195
+ noteSuccess(now = Date.now()) {
1166
1196
  this.completed++;
1167
- this.streak++;
1168
1197
  this.consecutiveThrottlesAtMin = 0;
1169
- if (this.streak >= this.opts.increaseAfter && this.current < this.opts.max) {
1198
+ if (now < this.rampFrozenUntil) return;
1199
+ this.streak++;
1200
+ if (this.current >= this.opts.max) return;
1201
+ const target = this.current + 1;
1202
+ const needed = target >= this.softCeiling ? this.opts.increaseAfter * this.opts.throttleCeilingMultiplier : this.opts.increaseAfter;
1203
+ if (this.streak >= needed) {
1170
1204
  const previous = this.current;
1171
- this.current = Math.min(this.opts.max, this.current + 1);
1205
+ this.current = Math.min(this.opts.max, target);
1172
1206
  this.streak = 0;
1207
+ if (this.current >= this.softCeiling) {
1208
+ this.softCeiling = NO_SOFT_CEILING;
1209
+ }
1173
1210
  this.notify("ramp-up", previous);
1174
1211
  }
1175
1212
  }
@@ -1181,8 +1218,10 @@ var AdaptiveConcurrencyController = class {
1181
1218
  const previous = this.current;
1182
1219
  const next = Math.max(this.opts.min, Math.floor(this.current / 2));
1183
1220
  this.cooldownUntil = now + this.opts.cooldownMs;
1221
+ this.rampFrozenUntil = now + this.opts.rampUpCooldownMs;
1184
1222
  if (next !== previous) {
1185
1223
  this.current = next;
1224
+ this.softCeiling = previous;
1186
1225
  this.notify("throttle", previous);
1187
1226
  }
1188
1227
  if (this.current === this.opts.min) {
@@ -1191,6 +1230,15 @@ var AdaptiveConcurrencyController = class {
1191
1230
  this.consecutiveThrottlesAtMin = 0;
1192
1231
  }
1193
1232
  }
1233
+ /**
1234
+ * Current soft ceiling — the highest concurrency at which a throttle was
1235
+ * observed and the controller halved. {@link Number.POSITIVE_INFINITY} when
1236
+ * no ceiling is in effect. Re-approaching this level requires
1237
+ * `increaseAfter * throttleCeilingMultiplier` consecutive successes.
1238
+ */
1239
+ getSoftCeiling() {
1240
+ return this.softCeiling;
1241
+ }
1194
1242
  /** Configured circuit-breaker threshold. */
1195
1243
  getMaxConsecutiveThrottlesAtMin() {
1196
1244
  return this.opts.maxConsecutiveThrottlesAtMin;
@@ -1371,7 +1419,7 @@ async function importBulk(conn, repositoryId, data, options) {
1371
1419
  let relationshipsImported = 0;
1372
1420
  const errors = [];
1373
1421
  const skipCheck = options?.skipExistenceCheck ?? false;
1374
- const controller = new AdaptiveConcurrencyController(options?.adaptiveConcurrency);
1422
+ const controller = resolveController(options?.adaptiveConcurrency, options?.adaptiveConcurrencyHandle);
1375
1423
  for (const chunk of data) {
1376
1424
  if (chunk.entities && chunk.entities.length > 0) {
1377
1425
  const results = await runAdaptive(
@@ -2136,35 +2184,9 @@ var CosmosDbProvider = class {
2136
2184
  return this.track("executeNativeQuery", void 0, () => this.executeNativeQueryImpl(query, params));
2137
2185
  }
2138
2186
  async executeNativeQueryImpl(query, params) {
2139
- const startTime = Date.now();
2140
2187
  try {
2141
2188
  const result = await this.conn.submit(query, params);
2142
- const executionTimeMs = Date.now() - startTime;
2143
- const entities = [];
2144
- for (const item of result.items) {
2145
- try {
2146
- const entity = entityFromGremlin(item);
2147
- if (entity.id) {
2148
- entities.push(entity);
2149
- }
2150
- } catch {
2151
- }
2152
- }
2153
- const queryMetadata = {
2154
- executionTimeMs,
2155
- resourceCost: result.requestCharge != null ? { units: "RU", value: result.requestCharge } : void 0,
2156
- compiledQuery: query,
2157
- compiledQueryLanguage: "gremlin",
2158
- appliedLimits: { maxResults: entities.length },
2159
- truncated: false
2160
- };
2161
- return {
2162
- entities,
2163
- total: entities.length,
2164
- returned: entities.length,
2165
- hasMore: false,
2166
- queryMetadata
2167
- };
2189
+ return result.items;
2168
2190
  } catch (err) {
2169
2191
  throw new ProviderError(
2170
2192
  `Native Gremlin query failed: ${err instanceof Error ? err.message : String(err)}`,