@hot-updater/plugin-insights 1.0.0-rc.29 → 1.0.0-rc.30
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/server/contract.cjs +24 -3
- package/dist/server/contract.mjs +24 -3
- package/dist/server/index.cjs +28 -1
- package/dist/server/index.d.cts +66 -2
- package/dist/server/index.d.mts +66 -2
- package/dist/server/index.mjs +29 -2
- package/dist/server/model.cjs +2 -1
- package/dist/server/model.mjs +2 -1
- package/dist/server/modelTypes.d.cts +32 -2
- package/dist/server/modelTypes.d.mts +32 -2
- package/dist/server/reads.cjs +55 -6
- package/dist/server/reads.mjs +55 -7
- package/dist/server/recordEvent.cjs +50 -4
- package/dist/server/recordEvent.mjs +50 -4
- package/dist/server/schema.cjs +67 -0
- package/dist/server/schema.d.cts +124 -0
- package/dist/server/schema.d.mts +124 -0
- package/dist/server/schema.mjs +67 -0
- package/package.json +2 -2
package/dist/server/reads.cjs
CHANGED
|
@@ -345,19 +345,30 @@ const lifetimeMetrics = async (db, release) => {
|
|
|
345
345
|
failedLaunches: lifetime?.failed_launches ?? 0
|
|
346
346
|
};
|
|
347
347
|
};
|
|
348
|
-
/**
|
|
349
|
-
|
|
348
|
+
/**
|
|
349
|
+
* Counters, unique users, and a series over a window: a point per UTC day
|
|
350
|
+
* with rows, or, with `intervalMs`, every point of that span from the
|
|
351
|
+
* window's start.
|
|
352
|
+
*/
|
|
353
|
+
const rangedMetrics = async (db, parts, users, range, days, hour, intervalMs) => {
|
|
350
354
|
const [counters, sketches] = await Promise.all([counterRows(db, parts, range, days, hour), sketchRows(db, users.parts, range, days, hour)]);
|
|
351
355
|
const series = /* @__PURE__ */ new Map();
|
|
356
|
+
if (intervalMs !== void 0) for (let start = range.start; start < range.end; start += intervalMs) series.set(start, {
|
|
357
|
+
downloads: 0,
|
|
358
|
+
launches: 0,
|
|
359
|
+
failedLaunches: 0
|
|
360
|
+
});
|
|
352
361
|
for (const row of counters) {
|
|
353
|
-
const
|
|
354
|
-
const point = series.get(
|
|
362
|
+
const start = intervalMs === void 0 ? dayFloor(row.bucket_start_ms) : row.bucket_start_ms - (row.bucket_start_ms - range.start) % intervalMs;
|
|
363
|
+
const point = series.get(start) ?? {
|
|
364
|
+
downloads: 0,
|
|
355
365
|
launches: 0,
|
|
356
366
|
failedLaunches: 0
|
|
357
367
|
};
|
|
368
|
+
point.downloads += row.downloads;
|
|
358
369
|
point.launches += row.launches;
|
|
359
370
|
point.failedLaunches += row.failed_launches;
|
|
360
|
-
series.set(
|
|
371
|
+
series.set(start, point);
|
|
361
372
|
}
|
|
362
373
|
const total = (field) => counters.reduce((sum, row) => sum + row[field], 0);
|
|
363
374
|
return {
|
|
@@ -403,7 +414,7 @@ const getReleaseActivity = async (db, input, now, retention) => {
|
|
|
403
414
|
metrics: input.timeRange === void 0 ? await lifetimeMetrics(db, release) : await rangedMetrics(db, releaseParts(release), {
|
|
404
415
|
parts: releaseParts(release),
|
|
405
416
|
field: "launch_users"
|
|
406
|
-
}, input.timeRange, false, kept.hour)
|
|
417
|
+
}, input.timeRange, false, kept.hour, input.intervalMs)
|
|
407
418
|
})));
|
|
408
419
|
const oldest = kept[input.scope === void 0 ? "hour" : "day"];
|
|
409
420
|
return {
|
|
@@ -669,12 +680,50 @@ const getUpdateFailures = async (db, input, now, retention) => {
|
|
|
669
680
|
}
|
|
670
681
|
};
|
|
671
682
|
};
|
|
683
|
+
/** Reads daily gauges, never raw events or mutable latest-state distribution. */
|
|
684
|
+
const getDistributionHistory = async (db, input, now, retention) => {
|
|
685
|
+
const at = now();
|
|
686
|
+
const { start, end } = input.timeRange;
|
|
687
|
+
const points = /* @__PURE__ */ new Map();
|
|
688
|
+
for (let day = start; day < end; day += require_schema.DAY_MS) points.set(day, []);
|
|
689
|
+
const rows = await drain((page) => db.findAggregates("insights_distribution_history", {
|
|
690
|
+
index: "byScope",
|
|
691
|
+
where: {
|
|
692
|
+
channel: input.channel,
|
|
693
|
+
platform: input.platform
|
|
694
|
+
},
|
|
695
|
+
range: {
|
|
696
|
+
gte: start,
|
|
697
|
+
lt: end
|
|
698
|
+
},
|
|
699
|
+
limit: PAGE,
|
|
700
|
+
...page
|
|
701
|
+
}));
|
|
702
|
+
for (const row of rows) {
|
|
703
|
+
if (row.installations <= 0) continue;
|
|
704
|
+
points.get(row.bucket_start_ms).push({
|
|
705
|
+
appVersion: row.app_version,
|
|
706
|
+
releaseId: row.release_id || null,
|
|
707
|
+
bundleKind: row.bundle_kind,
|
|
708
|
+
installations: row.installations
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
return {
|
|
712
|
+
coverage: coverageOf(start, retained(at, retention).day),
|
|
713
|
+
measuredAtMs: at,
|
|
714
|
+
points: [...points].map(([startMs, bundles]) => ({
|
|
715
|
+
startMs,
|
|
716
|
+
bundles
|
|
717
|
+
}))
|
|
718
|
+
};
|
|
719
|
+
};
|
|
672
720
|
//#endregion
|
|
673
721
|
exports.UPDATE_FAILURES_RANGE_MS = UPDATE_FAILURES_RANGE_MS;
|
|
674
722
|
exports.countEvents = countEvents;
|
|
675
723
|
exports.countLatestEvents = countLatestEvents;
|
|
676
724
|
exports.findLatestEvents = findLatestEvents;
|
|
677
725
|
exports.getAppUsage = getAppUsage;
|
|
726
|
+
exports.getDistributionHistory = getDistributionHistory;
|
|
678
727
|
exports.getReleaseActivity = getReleaseActivity;
|
|
679
728
|
exports.getUpdateFailures = getUpdateFailures;
|
|
680
729
|
exports.listEvents = listEvents;
|
package/dist/server/reads.mjs
CHANGED
|
@@ -345,19 +345,30 @@ const lifetimeMetrics = async (db, release) => {
|
|
|
345
345
|
failedLaunches: lifetime?.failed_launches ?? 0
|
|
346
346
|
};
|
|
347
347
|
};
|
|
348
|
-
/**
|
|
349
|
-
|
|
348
|
+
/**
|
|
349
|
+
* Counters, unique users, and a series over a window: a point per UTC day
|
|
350
|
+
* with rows, or, with `intervalMs`, every point of that span from the
|
|
351
|
+
* window's start.
|
|
352
|
+
*/
|
|
353
|
+
const rangedMetrics = async (db, parts, users, range, days, hour, intervalMs) => {
|
|
350
354
|
const [counters, sketches] = await Promise.all([counterRows(db, parts, range, days, hour), sketchRows(db, users.parts, range, days, hour)]);
|
|
351
355
|
const series = /* @__PURE__ */ new Map();
|
|
356
|
+
if (intervalMs !== void 0) for (let start = range.start; start < range.end; start += intervalMs) series.set(start, {
|
|
357
|
+
downloads: 0,
|
|
358
|
+
launches: 0,
|
|
359
|
+
failedLaunches: 0
|
|
360
|
+
});
|
|
352
361
|
for (const row of counters) {
|
|
353
|
-
const
|
|
354
|
-
const point = series.get(
|
|
362
|
+
const start = intervalMs === void 0 ? dayFloor(row.bucket_start_ms) : row.bucket_start_ms - (row.bucket_start_ms - range.start) % intervalMs;
|
|
363
|
+
const point = series.get(start) ?? {
|
|
364
|
+
downloads: 0,
|
|
355
365
|
launches: 0,
|
|
356
366
|
failedLaunches: 0
|
|
357
367
|
};
|
|
368
|
+
point.downloads += row.downloads;
|
|
358
369
|
point.launches += row.launches;
|
|
359
370
|
point.failedLaunches += row.failed_launches;
|
|
360
|
-
series.set(
|
|
371
|
+
series.set(start, point);
|
|
361
372
|
}
|
|
362
373
|
const total = (field) => counters.reduce((sum, row) => sum + row[field], 0);
|
|
363
374
|
return {
|
|
@@ -403,7 +414,7 @@ const getReleaseActivity = async (db, input, now, retention) => {
|
|
|
403
414
|
metrics: input.timeRange === void 0 ? await lifetimeMetrics(db, release) : await rangedMetrics(db, releaseParts(release), {
|
|
404
415
|
parts: releaseParts(release),
|
|
405
416
|
field: "launch_users"
|
|
406
|
-
}, input.timeRange, false, kept.hour)
|
|
417
|
+
}, input.timeRange, false, kept.hour, input.intervalMs)
|
|
407
418
|
})));
|
|
408
419
|
const oldest = kept[input.scope === void 0 ? "hour" : "day"];
|
|
409
420
|
return {
|
|
@@ -669,5 +680,42 @@ const getUpdateFailures = async (db, input, now, retention) => {
|
|
|
669
680
|
}
|
|
670
681
|
};
|
|
671
682
|
};
|
|
683
|
+
/** Reads daily gauges, never raw events or mutable latest-state distribution. */
|
|
684
|
+
const getDistributionHistory = async (db, input, now, retention) => {
|
|
685
|
+
const at = now();
|
|
686
|
+
const { start, end } = input.timeRange;
|
|
687
|
+
const points = /* @__PURE__ */ new Map();
|
|
688
|
+
for (let day = start; day < end; day += DAY_MS) points.set(day, []);
|
|
689
|
+
const rows = await drain((page) => db.findAggregates("insights_distribution_history", {
|
|
690
|
+
index: "byScope",
|
|
691
|
+
where: {
|
|
692
|
+
channel: input.channel,
|
|
693
|
+
platform: input.platform
|
|
694
|
+
},
|
|
695
|
+
range: {
|
|
696
|
+
gte: start,
|
|
697
|
+
lt: end
|
|
698
|
+
},
|
|
699
|
+
limit: PAGE,
|
|
700
|
+
...page
|
|
701
|
+
}));
|
|
702
|
+
for (const row of rows) {
|
|
703
|
+
if (row.installations <= 0) continue;
|
|
704
|
+
points.get(row.bucket_start_ms).push({
|
|
705
|
+
appVersion: row.app_version,
|
|
706
|
+
releaseId: row.release_id || null,
|
|
707
|
+
bundleKind: row.bundle_kind,
|
|
708
|
+
installations: row.installations
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
return {
|
|
712
|
+
coverage: coverageOf(start, retained(at, retention).day),
|
|
713
|
+
measuredAtMs: at,
|
|
714
|
+
points: [...points].map(([startMs, bundles]) => ({
|
|
715
|
+
startMs,
|
|
716
|
+
bundles
|
|
717
|
+
}))
|
|
718
|
+
};
|
|
719
|
+
};
|
|
672
720
|
//#endregion
|
|
673
|
-
export { UPDATE_FAILURES_RANGE_MS, countEvents, countLatestEvents, findLatestEvents, getAppUsage, getReleaseActivity, getUpdateFailures, listEvents };
|
|
721
|
+
export { UPDATE_FAILURES_RANGE_MS, countEvents, countLatestEvents, findLatestEvents, getAppUsage, getDistributionHistory, getReleaseActivity, getUpdateFailures, listEvents };
|
|
@@ -2,6 +2,7 @@ const require_contract = require("./contract.cjs");
|
|
|
2
2
|
const require_overview = require("./overview.cjs");
|
|
3
3
|
const require_schema = require("./schema.cjs");
|
|
4
4
|
let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
|
|
5
|
+
let _hot_updater_protocol = require("@hot-updater/protocol");
|
|
5
6
|
//#region src/server/recordEvent.ts
|
|
6
7
|
const hourOf = (ms) => ms - ms % require_schema.HOUR_MS;
|
|
7
8
|
const dayOf = (ms) => ms - ms % require_schema.DAY_MS;
|
|
@@ -276,21 +277,66 @@ const REPEATED_FIELDS = [
|
|
|
276
277
|
const repeatsHead = (event, head) => event.type === "UNCHANGED" && head.type !== "UPDATE_DOWNLOADED" && dayOf(event.received_at_ms) === dayOf(head.received_at_ms) && REPEATED_FIELDS.every((field) => event[field] === head[field]);
|
|
277
278
|
/**
|
|
278
279
|
* Records one event in one transaction: one batch read of the event and its
|
|
279
|
-
* installation's
|
|
280
|
+
* installation's latest and daily heads, then the gauge and sketch rows it
|
|
281
|
+
* changes and one
|
|
280
282
|
* write. On a database that batches aggregates (DynamoDB, Firestore), the
|
|
281
283
|
* write holds the event's rows and one log row instead, and a compaction
|
|
282
284
|
* reads and writes the aggregate rows. A stored event's id changes nothing
|
|
283
285
|
* when repeated, whichever installation sends it, and neither does the id
|
|
284
286
|
* of the installation's head. An UNCHANGED report that repeats its head on
|
|
285
|
-
* the same UTC day
|
|
287
|
+
* the same UTC day only advances its daily observation. An older event counts in its
|
|
286
288
|
* own hour but never replaces the head. An update failure is stored and
|
|
287
289
|
* counted, but changes what no installation runs, so it moves no head.
|
|
288
290
|
*/
|
|
289
291
|
const recordEvent = (db, event) => {
|
|
290
292
|
require_contract.assertBundleEventRow(event);
|
|
291
293
|
return db.transaction(async (tx) => {
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
+
const dailyKey = {
|
|
295
|
+
install_id: event.install_id,
|
|
296
|
+
bucket_start_ms: dayOf(event.received_at_ms)
|
|
297
|
+
};
|
|
298
|
+
const [existing, previous, daily] = await Promise.all([
|
|
299
|
+
tx.findOne("bundle_events", { id: event.id }),
|
|
300
|
+
tx.findOne("bundle_event_heads", { install_id: event.install_id }),
|
|
301
|
+
event.type === "UPDATE_FAILED" ? null : tx.findOne("bundle_daily_heads", dailyKey)
|
|
302
|
+
]);
|
|
303
|
+
if (existing !== null || previous?.id === event.id || daily?.id === event.id) return;
|
|
304
|
+
if (event.type !== "UPDATE_FAILED" && (daily === null || isNewer(event, daily))) {
|
|
305
|
+
const downloaded = event.type === "UPDATE_DOWNLOADED";
|
|
306
|
+
const releaseId = downloaded ? event.from_release_id : event.to_release_id;
|
|
307
|
+
const bundleId = downloaded ? event.from_bundle_id : event.to_bundle_id;
|
|
308
|
+
const next = {
|
|
309
|
+
...dailyKey,
|
|
310
|
+
id: event.id,
|
|
311
|
+
received_at_ms: event.received_at_ms,
|
|
312
|
+
channel: event.channel,
|
|
313
|
+
platform: event.platform,
|
|
314
|
+
app_version: event.app_version,
|
|
315
|
+
release_id: releaseId ?? "",
|
|
316
|
+
bundle_kind: bundleId === _hot_updater_protocol.NIL_UUID ? "builtin" : releaseId === null ? "unknown" : "release"
|
|
317
|
+
};
|
|
318
|
+
const count = (head, delta) => {
|
|
319
|
+
tx.aggregate("insights_distribution_history", {
|
|
320
|
+
channel: head.channel,
|
|
321
|
+
platform: head.platform,
|
|
322
|
+
app_version: head.app_version,
|
|
323
|
+
release_id: head.release_id,
|
|
324
|
+
bundle_kind: head.bundle_kind,
|
|
325
|
+
bucket_start_ms: head.bucket_start_ms
|
|
326
|
+
}, { installations: delta }, { shardBy: head.install_id });
|
|
327
|
+
};
|
|
328
|
+
if (daily !== null) {
|
|
329
|
+
if (daily.channel !== next.channel || daily.platform !== next.platform || daily.app_version !== next.app_version || daily.release_id !== next.release_id || daily.bundle_kind !== next.bundle_kind) {
|
|
330
|
+
count(daily, -1);
|
|
331
|
+
count(next, 1);
|
|
332
|
+
}
|
|
333
|
+
const { install_id: _, bucket_start_ms: __, ...fields } = next;
|
|
334
|
+
tx.update("bundle_daily_heads", daily, fields);
|
|
335
|
+
} else {
|
|
336
|
+
tx.create("bundle_daily_heads", next);
|
|
337
|
+
count(next, 1);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
294
340
|
if (previous !== null && repeatsHead(event, previous)) return;
|
|
295
341
|
if (event.type !== "UNCHANGED") {
|
|
296
342
|
tx.create("bundle_events", event);
|
|
@@ -2,6 +2,7 @@ import { assertBundleEventRow } from "./contract.mjs";
|
|
|
2
2
|
import { insightsKey, insightsOverviewDeltas, insightsOverviewId } from "./overview.mjs";
|
|
3
3
|
import { DAILY_EVENTS, DAY_MS, HOUR_MS, isFailedCheck } from "./schema.mjs";
|
|
4
4
|
import { addDistinct, compareUtf8 } from "@hot-updater/plugin-core";
|
|
5
|
+
import { NIL_UUID } from "@hot-updater/protocol";
|
|
5
6
|
//#region src/server/recordEvent.ts
|
|
6
7
|
const hourOf = (ms) => ms - ms % HOUR_MS;
|
|
7
8
|
const dayOf = (ms) => ms - ms % DAY_MS;
|
|
@@ -276,21 +277,66 @@ const REPEATED_FIELDS = [
|
|
|
276
277
|
const repeatsHead = (event, head) => event.type === "UNCHANGED" && head.type !== "UPDATE_DOWNLOADED" && dayOf(event.received_at_ms) === dayOf(head.received_at_ms) && REPEATED_FIELDS.every((field) => event[field] === head[field]);
|
|
277
278
|
/**
|
|
278
279
|
* Records one event in one transaction: one batch read of the event and its
|
|
279
|
-
* installation's
|
|
280
|
+
* installation's latest and daily heads, then the gauge and sketch rows it
|
|
281
|
+
* changes and one
|
|
280
282
|
* write. On a database that batches aggregates (DynamoDB, Firestore), the
|
|
281
283
|
* write holds the event's rows and one log row instead, and a compaction
|
|
282
284
|
* reads and writes the aggregate rows. A stored event's id changes nothing
|
|
283
285
|
* when repeated, whichever installation sends it, and neither does the id
|
|
284
286
|
* of the installation's head. An UNCHANGED report that repeats its head on
|
|
285
|
-
* the same UTC day
|
|
287
|
+
* the same UTC day only advances its daily observation. An older event counts in its
|
|
286
288
|
* own hour but never replaces the head. An update failure is stored and
|
|
287
289
|
* counted, but changes what no installation runs, so it moves no head.
|
|
288
290
|
*/
|
|
289
291
|
const recordEvent = (db, event) => {
|
|
290
292
|
assertBundleEventRow(event);
|
|
291
293
|
return db.transaction(async (tx) => {
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
+
const dailyKey = {
|
|
295
|
+
install_id: event.install_id,
|
|
296
|
+
bucket_start_ms: dayOf(event.received_at_ms)
|
|
297
|
+
};
|
|
298
|
+
const [existing, previous, daily] = await Promise.all([
|
|
299
|
+
tx.findOne("bundle_events", { id: event.id }),
|
|
300
|
+
tx.findOne("bundle_event_heads", { install_id: event.install_id }),
|
|
301
|
+
event.type === "UPDATE_FAILED" ? null : tx.findOne("bundle_daily_heads", dailyKey)
|
|
302
|
+
]);
|
|
303
|
+
if (existing !== null || previous?.id === event.id || daily?.id === event.id) return;
|
|
304
|
+
if (event.type !== "UPDATE_FAILED" && (daily === null || isNewer(event, daily))) {
|
|
305
|
+
const downloaded = event.type === "UPDATE_DOWNLOADED";
|
|
306
|
+
const releaseId = downloaded ? event.from_release_id : event.to_release_id;
|
|
307
|
+
const bundleId = downloaded ? event.from_bundle_id : event.to_bundle_id;
|
|
308
|
+
const next = {
|
|
309
|
+
...dailyKey,
|
|
310
|
+
id: event.id,
|
|
311
|
+
received_at_ms: event.received_at_ms,
|
|
312
|
+
channel: event.channel,
|
|
313
|
+
platform: event.platform,
|
|
314
|
+
app_version: event.app_version,
|
|
315
|
+
release_id: releaseId ?? "",
|
|
316
|
+
bundle_kind: bundleId === NIL_UUID ? "builtin" : releaseId === null ? "unknown" : "release"
|
|
317
|
+
};
|
|
318
|
+
const count = (head, delta) => {
|
|
319
|
+
tx.aggregate("insights_distribution_history", {
|
|
320
|
+
channel: head.channel,
|
|
321
|
+
platform: head.platform,
|
|
322
|
+
app_version: head.app_version,
|
|
323
|
+
release_id: head.release_id,
|
|
324
|
+
bundle_kind: head.bundle_kind,
|
|
325
|
+
bucket_start_ms: head.bucket_start_ms
|
|
326
|
+
}, { installations: delta }, { shardBy: head.install_id });
|
|
327
|
+
};
|
|
328
|
+
if (daily !== null) {
|
|
329
|
+
if (daily.channel !== next.channel || daily.platform !== next.platform || daily.app_version !== next.app_version || daily.release_id !== next.release_id || daily.bundle_kind !== next.bundle_kind) {
|
|
330
|
+
count(daily, -1);
|
|
331
|
+
count(next, 1);
|
|
332
|
+
}
|
|
333
|
+
const { install_id: _, bucket_start_ms: __, ...fields } = next;
|
|
334
|
+
tx.update("bundle_daily_heads", daily, fields);
|
|
335
|
+
} else {
|
|
336
|
+
tx.create("bundle_daily_heads", next);
|
|
337
|
+
count(next, 1);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
294
340
|
if (previous !== null && repeatsHead(event, previous)) return;
|
|
295
341
|
if (event.type !== "UNCHANGED") {
|
|
296
342
|
tx.create("bundle_events", event);
|
package/dist/server/schema.cjs
CHANGED
|
@@ -354,6 +354,71 @@ const insightsDistribution = (retention) => (0, _hot_updater_plugin_core.defineA
|
|
|
354
354
|
},
|
|
355
355
|
retention
|
|
356
356
|
});
|
|
357
|
+
/** The last running bundle observed per installation and UTC day. */
|
|
358
|
+
const bundleDailyHeads = (retention) => (0, _hot_updater_plugin_core.defineTable)({
|
|
359
|
+
install_id: {
|
|
360
|
+
type: "string",
|
|
361
|
+
maxLength: 255
|
|
362
|
+
},
|
|
363
|
+
bucket_start_ms: { type: "integer" },
|
|
364
|
+
id: {
|
|
365
|
+
type: "string",
|
|
366
|
+
maxLength: 36
|
|
367
|
+
},
|
|
368
|
+
received_at_ms: { type: "integer" },
|
|
369
|
+
channel: { type: "string" },
|
|
370
|
+
platform: {
|
|
371
|
+
type: "string",
|
|
372
|
+
maxLength: 16
|
|
373
|
+
},
|
|
374
|
+
app_version: { type: "string" },
|
|
375
|
+
release_id: {
|
|
376
|
+
type: "string",
|
|
377
|
+
maxLength: 36
|
|
378
|
+
},
|
|
379
|
+
bundle_kind: {
|
|
380
|
+
type: "string",
|
|
381
|
+
maxLength: 16
|
|
382
|
+
}
|
|
383
|
+
}, {
|
|
384
|
+
key: ["install_id", "bucket_start_ms"],
|
|
385
|
+
retention
|
|
386
|
+
});
|
|
387
|
+
/** Daily observations stay in their day when an installation reports again. */
|
|
388
|
+
const insightsDistributionHistory = (retention) => (0, _hot_updater_plugin_core.defineAggregate)({
|
|
389
|
+
channel: { type: "string" },
|
|
390
|
+
platform: {
|
|
391
|
+
type: "string",
|
|
392
|
+
maxLength: 16
|
|
393
|
+
},
|
|
394
|
+
app_version: { type: "string" },
|
|
395
|
+
release_id: {
|
|
396
|
+
type: "string",
|
|
397
|
+
maxLength: 36
|
|
398
|
+
},
|
|
399
|
+
bundle_kind: {
|
|
400
|
+
type: "string",
|
|
401
|
+
maxLength: 16
|
|
402
|
+
},
|
|
403
|
+
bucket_start_ms: { type: "integer" }
|
|
404
|
+
}, {
|
|
405
|
+
key: [
|
|
406
|
+
"channel",
|
|
407
|
+
"platform",
|
|
408
|
+
"app_version",
|
|
409
|
+
"release_id",
|
|
410
|
+
"bundle_kind",
|
|
411
|
+
"bucket_start_ms"
|
|
412
|
+
],
|
|
413
|
+
gauges: ["installations"],
|
|
414
|
+
shards: GAUGE_SHARDS,
|
|
415
|
+
batched: true,
|
|
416
|
+
indexes: { byScope: {
|
|
417
|
+
eq: ["channel", "platform"],
|
|
418
|
+
sort: ["bucket_start_ms"]
|
|
419
|
+
} },
|
|
420
|
+
retention
|
|
421
|
+
});
|
|
357
422
|
/** Latest events by the bundle they came from or went to, bucketed like the distribution. */
|
|
358
423
|
const insightsLatestByBundle = (retention) => (0, _hot_updater_plugin_core.defineAggregate)({
|
|
359
424
|
platform: {
|
|
@@ -473,6 +538,8 @@ const createInsightsSchema = ({ rawDays, dailyDays } = DEFAULT_INSIGHTS_RETENTIO
|
|
|
473
538
|
insights_overview_lifetime: counters(),
|
|
474
539
|
insights_sketches_lifetime: lifetimeSketches(),
|
|
475
540
|
insights_distribution: insightsDistribution(daily),
|
|
541
|
+
bundle_daily_heads: bundleDailyHeads(daily),
|
|
542
|
+
insights_distribution_history: insightsDistributionHistory(daily),
|
|
476
543
|
insights_latest_by_bundle: insightsLatestByBundle(daily),
|
|
477
544
|
insights_outcomes: insightsOutcomes(hourly),
|
|
478
545
|
insights_failures: insightsFailures(hourly)
|
package/dist/server/schema.d.cts
CHANGED
|
@@ -263,6 +263,68 @@ declare const createInsightsSchema: ({ rawDays, dailyDays }?: InsightsRetention)
|
|
|
263
263
|
readonly sort: readonly ["bucket_start_ms"];
|
|
264
264
|
};
|
|
265
265
|
}, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
|
|
266
|
+
readonly bundle_daily_heads: import("@hot-updater/plugin-core").TableDefinition<{
|
|
267
|
+
readonly install_id: {
|
|
268
|
+
readonly type: "string";
|
|
269
|
+
readonly maxLength: 255;
|
|
270
|
+
};
|
|
271
|
+
readonly bucket_start_ms: {
|
|
272
|
+
readonly type: "integer";
|
|
273
|
+
};
|
|
274
|
+
readonly id: {
|
|
275
|
+
readonly type: "string";
|
|
276
|
+
readonly maxLength: 36;
|
|
277
|
+
};
|
|
278
|
+
readonly received_at_ms: {
|
|
279
|
+
readonly type: "integer";
|
|
280
|
+
};
|
|
281
|
+
readonly channel: {
|
|
282
|
+
readonly type: "string";
|
|
283
|
+
};
|
|
284
|
+
readonly platform: {
|
|
285
|
+
readonly type: "string";
|
|
286
|
+
readonly maxLength: 16;
|
|
287
|
+
};
|
|
288
|
+
readonly app_version: {
|
|
289
|
+
readonly type: "string";
|
|
290
|
+
};
|
|
291
|
+
readonly release_id: {
|
|
292
|
+
readonly type: "string";
|
|
293
|
+
readonly maxLength: 36;
|
|
294
|
+
};
|
|
295
|
+
readonly bundle_kind: {
|
|
296
|
+
readonly type: "string";
|
|
297
|
+
readonly maxLength: 16;
|
|
298
|
+
};
|
|
299
|
+
}, Readonly<Record<string, import("@hot-updater/plugin-core").FieldType>>, {}, readonly ["install_id", "bucket_start_ms"]>;
|
|
300
|
+
readonly insights_distribution_history: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
301
|
+
readonly channel: {
|
|
302
|
+
readonly type: "string";
|
|
303
|
+
};
|
|
304
|
+
readonly platform: {
|
|
305
|
+
readonly type: "string";
|
|
306
|
+
readonly maxLength: 16;
|
|
307
|
+
};
|
|
308
|
+
readonly app_version: {
|
|
309
|
+
readonly type: "string";
|
|
310
|
+
};
|
|
311
|
+
readonly release_id: {
|
|
312
|
+
readonly type: "string";
|
|
313
|
+
readonly maxLength: 36;
|
|
314
|
+
};
|
|
315
|
+
readonly bundle_kind: {
|
|
316
|
+
readonly type: "string";
|
|
317
|
+
readonly maxLength: 16;
|
|
318
|
+
};
|
|
319
|
+
readonly bucket_start_ms: {
|
|
320
|
+
readonly type: "integer";
|
|
321
|
+
};
|
|
322
|
+
}, "installations", {
|
|
323
|
+
readonly byScope: {
|
|
324
|
+
readonly eq: readonly ["channel", "platform"];
|
|
325
|
+
readonly sort: readonly ["bucket_start_ms"];
|
|
326
|
+
};
|
|
327
|
+
}, never, readonly ["channel", "platform", "app_version", "release_id", "bundle_kind", "bucket_start_ms"]>;
|
|
266
328
|
readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
267
329
|
readonly platform: {
|
|
268
330
|
readonly type: "string";
|
|
@@ -602,6 +664,68 @@ declare const insightsSchema: {
|
|
|
602
664
|
readonly sort: readonly ["bucket_start_ms"];
|
|
603
665
|
};
|
|
604
666
|
}, never, readonly ["channel", "platform", "app_version", "release_id", "bucket_start_ms"]>;
|
|
667
|
+
readonly bundle_daily_heads: import("@hot-updater/plugin-core").TableDefinition<{
|
|
668
|
+
readonly install_id: {
|
|
669
|
+
readonly type: "string";
|
|
670
|
+
readonly maxLength: 255;
|
|
671
|
+
};
|
|
672
|
+
readonly bucket_start_ms: {
|
|
673
|
+
readonly type: "integer";
|
|
674
|
+
};
|
|
675
|
+
readonly id: {
|
|
676
|
+
readonly type: "string";
|
|
677
|
+
readonly maxLength: 36;
|
|
678
|
+
};
|
|
679
|
+
readonly received_at_ms: {
|
|
680
|
+
readonly type: "integer";
|
|
681
|
+
};
|
|
682
|
+
readonly channel: {
|
|
683
|
+
readonly type: "string";
|
|
684
|
+
};
|
|
685
|
+
readonly platform: {
|
|
686
|
+
readonly type: "string";
|
|
687
|
+
readonly maxLength: 16;
|
|
688
|
+
};
|
|
689
|
+
readonly app_version: {
|
|
690
|
+
readonly type: "string";
|
|
691
|
+
};
|
|
692
|
+
readonly release_id: {
|
|
693
|
+
readonly type: "string";
|
|
694
|
+
readonly maxLength: 36;
|
|
695
|
+
};
|
|
696
|
+
readonly bundle_kind: {
|
|
697
|
+
readonly type: "string";
|
|
698
|
+
readonly maxLength: 16;
|
|
699
|
+
};
|
|
700
|
+
}, Readonly<Record<string, import("@hot-updater/plugin-core").FieldType>>, {}, readonly ["install_id", "bucket_start_ms"]>;
|
|
701
|
+
readonly insights_distribution_history: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
702
|
+
readonly channel: {
|
|
703
|
+
readonly type: "string";
|
|
704
|
+
};
|
|
705
|
+
readonly platform: {
|
|
706
|
+
readonly type: "string";
|
|
707
|
+
readonly maxLength: 16;
|
|
708
|
+
};
|
|
709
|
+
readonly app_version: {
|
|
710
|
+
readonly type: "string";
|
|
711
|
+
};
|
|
712
|
+
readonly release_id: {
|
|
713
|
+
readonly type: "string";
|
|
714
|
+
readonly maxLength: 36;
|
|
715
|
+
};
|
|
716
|
+
readonly bundle_kind: {
|
|
717
|
+
readonly type: "string";
|
|
718
|
+
readonly maxLength: 16;
|
|
719
|
+
};
|
|
720
|
+
readonly bucket_start_ms: {
|
|
721
|
+
readonly type: "integer";
|
|
722
|
+
};
|
|
723
|
+
}, "installations", {
|
|
724
|
+
readonly byScope: {
|
|
725
|
+
readonly eq: readonly ["channel", "platform"];
|
|
726
|
+
readonly sort: readonly ["bucket_start_ms"];
|
|
727
|
+
};
|
|
728
|
+
}, never, readonly ["channel", "platform", "app_version", "release_id", "bundle_kind", "bucket_start_ms"]>;
|
|
605
729
|
readonly insights_latest_by_bundle: import("@hot-updater/plugin-core").AggregateDefinition<{
|
|
606
730
|
readonly platform: {
|
|
607
731
|
readonly type: "string";
|