mirror of
https://github.com/saltbo/zpan.git
synced 2026-08-30 17:50:07 +08:00
fix(stats): honor traffic ledger history
This commit is contained in:
@@ -21,6 +21,7 @@ const MAX_BACKFILL_HOURS = 100_000
|
||||
const STATISTICS_OPENING_SOURCE_ID = 'v3-authoritative-sources'
|
||||
const STATISTICS_OPENING_EVENT_ID = `audit:statistics_source_initialized:${STATISTICS_OPENING_SOURCE_ID}`
|
||||
const STATISTICS_OPENING_OPTION_KEY = 'stats_integrity_exact_from_v3'
|
||||
const TRAFFIC_LEDGER_OPENING_EVENT_ID = 'traffic_ledger_opening_v1'
|
||||
|
||||
const statisticsExactFromMsSql = `COALESCE(
|
||||
(SELECT unixepoch(value) * 1000 FROM system_options WHERE key = '${STATISTICS_OPENING_OPTION_KEY}'),
|
||||
@@ -77,6 +78,7 @@ interface ValidationSummary {
|
||||
rawMissingByteEvents: number
|
||||
rollupMissingByteEvents: number
|
||||
invalidAuditEvents: number
|
||||
invalidIssuedTrafficReports: number
|
||||
missingUserRegistrationEvents: number
|
||||
invalidDownloadTaskEvents: number
|
||||
orphanRollupBuckets: number
|
||||
@@ -90,6 +92,9 @@ interface ValidationSummary {
|
||||
signupExpectedBuckets: number
|
||||
signupCompletedBuckets: number
|
||||
signupMissingBuckets: number
|
||||
trafficExpectedBuckets: number
|
||||
trafficCompletedBuckets: number
|
||||
trafficMissingBuckets: number
|
||||
userSignupProviderMismatchGroups: number
|
||||
openCounterMarkers: number
|
||||
}
|
||||
@@ -272,11 +277,19 @@ function buildHourlyBackfillSql(now: Date): string {
|
||||
const currentHour = Math.floor(now.getTime() / 3_600_000) * 3_600_000
|
||||
const fromMs = `MAX(${MIN_VALID_TIMESTAMP_MS}, COALESCE(${statisticsFirstFullHourMsSql}, ${MIN_VALID_TIMESTAMP_MS}))`
|
||||
const signupFromMs = userSignupHistoryStartSql(currentHour)
|
||||
const trafficFromMs = trafficHistoryStartSql(currentHour)
|
||||
return [
|
||||
...buildAdminStatsCounterRollupInsertSqlStatements({
|
||||
fromMs,
|
||||
toMs: currentHour,
|
||||
metrics: ADMIN_STATS_FACT_COUNTER_METRICS.filter((metric) => metric !== M.userSignup),
|
||||
metrics: ADMIN_STATS_FACT_COUNTER_METRICS.filter(
|
||||
(metric) => metric !== M.userSignup && metric !== M.transferDownloadIssued,
|
||||
),
|
||||
}),
|
||||
...buildAdminStatsCounterRollupInsertSqlStatements({
|
||||
fromMs: trafficFromMs,
|
||||
toMs: currentHour,
|
||||
metrics: [M.transferDownloadIssued],
|
||||
}),
|
||||
...buildAdminStatsCounterRollupInsertSqlStatements({
|
||||
fromMs: signupFromMs,
|
||||
@@ -284,10 +297,22 @@ function buildHourlyBackfillSql(now: Date): string {
|
||||
metrics: [M.userSignup],
|
||||
}),
|
||||
rollupMarkerBackfillSql(now),
|
||||
metricRollupMarkerBackfillSql(now, M.transferDownloadIssued, trafficFromMs),
|
||||
metricRollupMarkerBackfillSql(now, M.userSignup, signupFromMs),
|
||||
].join(';\n\n')
|
||||
}
|
||||
|
||||
function trafficHistoryStartSql(currentHour: number): string {
|
||||
return `MAX(
|
||||
${MIN_VALID_TIMESTAMP_MS},
|
||||
COALESCE((
|
||||
SELECT CAST((created_at + 3599999) / 3600000 AS INTEGER) * 3600000
|
||||
FROM cloud_traffic_reports
|
||||
WHERE event_id = '${TRAFFIC_LEDGER_OPENING_EVENT_ID}'
|
||||
), ${currentHour})
|
||||
)`
|
||||
}
|
||||
|
||||
function userSignupHistoryStartSql(currentHour: number): string {
|
||||
return `MAX(
|
||||
${MIN_VALID_TIMESTAMP_MS},
|
||||
@@ -628,12 +653,12 @@ export function buildValidationSql(now = new Date()): string {
|
||||
'rawDownloadEvents', (
|
||||
SELECT COUNT(*) FROM cloud_traffic_reports
|
||||
WHERE issued_at IS NOT NULL AND status <> 'reversed'
|
||||
AND issued_at >= MAX(${MIN_VALID_TIMESTAMP_MS}, COALESCE(${statisticsFirstFullHourMsSql}, ${MIN_VALID_TIMESTAMP_MS})) AND issued_at < ${currentHour}
|
||||
AND issued_at >= ${trafficHistoryStartSql(currentHour)} AND issued_at < ${currentHour}
|
||||
),
|
||||
'rawDownloadBytes', (
|
||||
SELECT COALESCE(SUM(bytes), 0) FROM cloud_traffic_reports
|
||||
WHERE issued_at IS NOT NULL AND status <> 'reversed'
|
||||
AND issued_at >= MAX(${MIN_VALID_TIMESTAMP_MS}, COALESCE(${statisticsFirstFullHourMsSql}, ${MIN_VALID_TIMESTAMP_MS})) AND issued_at < ${currentHour}
|
||||
AND issued_at >= ${trafficHistoryStartSql(currentHour)} AND issued_at < ${currentHour}
|
||||
),
|
||||
'statisticsExactFrom', ${statisticsExactFromMsSql}
|
||||
) AS summary;`
|
||||
@@ -758,6 +783,22 @@ export function buildValidationSql(now = new Date()): string {
|
||||
))
|
||||
)
|
||||
),
|
||||
'invalidIssuedTrafficReports', (
|
||||
SELECT COUNT(*)
|
||||
FROM cloud_traffic_reports ctr
|
||||
WHERE ctr.issued_at >= ${trafficHistoryStartSql(currentHour)}
|
||||
AND ctr.issued_at < ${currentHour}
|
||||
AND (
|
||||
ctr.status = 'reversed'
|
||||
OR ctr.bytes < 0
|
||||
OR length(ctr.org_id) = 0
|
||||
OR length(ctr.source_id) = 0
|
||||
OR ctr.source NOT IN (
|
||||
'object_download', 'direct_share', 'landing_share',
|
||||
'image_hosting', 'custom_domain_image', 'webdav_download'
|
||||
)
|
||||
)
|
||||
),
|
||||
'missingUserRegistrationEvents', (
|
||||
SELECT COUNT(*)
|
||||
FROM user registered_user
|
||||
@@ -1102,12 +1143,26 @@ signup_markers AS MATERIALIZED (
|
||||
AND json_extract(metadata, '$.quality') = 'exact'
|
||||
ELSE 0 END = 1
|
||||
),
|
||||
traffic_markers AS MATERIALIZED (
|
||||
SELECT bucket_start
|
||||
FROM stats_rollups_hourly
|
||||
WHERE metric_key = 'stats.rollup_run' AND org_id = ''
|
||||
AND dimension_key = 'metric_key' AND dimension_value = '${M.transferDownloadIssued}'
|
||||
AND CASE WHEN json_valid(metadata) = 1 THEN
|
||||
json_extract(metadata, '$.version') = 3
|
||||
AND json_extract(metadata, '$.scope') IN ('counters', 'full')
|
||||
AND json_extract(metadata, '$.quality') = 'exact'
|
||||
ELSE 0 END = 1
|
||||
),
|
||||
coverage AS MATERIALIZED (
|
||||
SELECT ${statsHistoryStartSql()} AS start_at, ${latestClosedHour} AS end_at
|
||||
),
|
||||
signup_coverage AS MATERIALIZED (
|
||||
SELECT ${userSignupHistoryStartSql(currentHour)} AS start_at, ${latestClosedHour} AS end_at
|
||||
),
|
||||
traffic_coverage AS MATERIALIZED (
|
||||
SELECT ${trafficHistoryStartSql(currentHour)} AS start_at, ${latestClosedHour} AS end_at
|
||||
),
|
||||
coverage_counts AS MATERIALIZED (
|
||||
SELECT
|
||||
CASE WHEN start_at IS NULL OR start_at > end_at THEN 0
|
||||
@@ -1123,6 +1178,14 @@ signup_coverage_counts AS MATERIALIZED (
|
||||
CASE WHEN start_at IS NULL OR start_at > end_at THEN 0
|
||||
ELSE (SELECT COUNT(*) FROM signup_markers WHERE bucket_start BETWEEN start_at AND end_at) END AS completed
|
||||
FROM signup_coverage
|
||||
),
|
||||
traffic_coverage_counts AS MATERIALIZED (
|
||||
SELECT
|
||||
CASE WHEN start_at IS NULL OR start_at > end_at THEN 0
|
||||
ELSE CAST((end_at - start_at) / 3600000 AS INTEGER) + 1 END AS expected,
|
||||
CASE WHEN start_at IS NULL OR start_at > end_at THEN 0
|
||||
ELSE (SELECT COUNT(*) FROM traffic_markers WHERE bucket_start BETWEEN start_at AND end_at) END AS completed
|
||||
FROM traffic_coverage
|
||||
)
|
||||
SELECT json_object(
|
||||
'hourlyRollups', (SELECT COUNT(*) FROM counter_markers),
|
||||
@@ -1132,9 +1195,13 @@ SELECT json_object(
|
||||
'signupExpectedBuckets', (SELECT expected FROM signup_coverage_counts),
|
||||
'signupCompletedBuckets', (SELECT completed FROM signup_coverage_counts),
|
||||
'signupMissingBuckets', (SELECT expected - completed FROM signup_coverage_counts),
|
||||
'trafficExpectedBuckets', (SELECT expected FROM traffic_coverage_counts),
|
||||
'trafficCompletedBuckets', (SELECT completed FROM traffic_coverage_counts),
|
||||
'trafficMissingBuckets', (SELECT expected - completed FROM traffic_coverage_counts),
|
||||
'openCounterMarkers', (
|
||||
(SELECT COUNT(*) FROM counter_markers WHERE bucket_start >= ${currentHour})
|
||||
+ (SELECT COUNT(*) FROM signup_markers WHERE bucket_start >= ${currentHour})
|
||||
+ (SELECT COUNT(*) FROM traffic_markers WHERE bucket_start >= ${currentHour})
|
||||
)
|
||||
) AS summary;
|
||||
`
|
||||
@@ -1334,6 +1401,7 @@ export function assertBackfillValidation(summary: ValidationSummary): void {
|
||||
if (
|
||||
mismatches.length > 0 ||
|
||||
summary.invalidAuditEvents > 0 ||
|
||||
summary.invalidIssuedTrafficReports > 0 ||
|
||||
summary.missingUserRegistrationEvents > 0 ||
|
||||
summary.invalidDownloadTaskEvents > 0 ||
|
||||
summary.orphanRollupBuckets > 0 ||
|
||||
@@ -1344,12 +1412,14 @@ export function assertBackfillValidation(summary: ValidationSummary): void {
|
||||
summary.incompatibleUserSnapshotRows > 0 ||
|
||||
summary.counterMissingBuckets > 0 ||
|
||||
summary.signupMissingBuckets > 0 ||
|
||||
summary.trafficMissingBuckets > 0 ||
|
||||
summary.openCounterMarkers > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`admin_stats_validation_failed:${JSON.stringify({
|
||||
mismatches,
|
||||
invalidAuditEvents: summary.invalidAuditEvents,
|
||||
invalidIssuedTrafficReports: summary.invalidIssuedTrafficReports,
|
||||
missingUserRegistrationEvents: summary.missingUserRegistrationEvents,
|
||||
invalidDownloadTaskEvents: summary.invalidDownloadTaskEvents,
|
||||
orphanRollupBuckets: summary.orphanRollupBuckets,
|
||||
@@ -1360,6 +1430,7 @@ export function assertBackfillValidation(summary: ValidationSummary): void {
|
||||
incompatibleUserSnapshotRows: summary.incompatibleUserSnapshotRows,
|
||||
counterMissingBuckets: summary.counterMissingBuckets,
|
||||
signupMissingBuckets: summary.signupMissingBuckets,
|
||||
trafficMissingBuckets: summary.trafficMissingBuckets,
|
||||
openCounterMarkers: summary.openCounterMarkers,
|
||||
})}`,
|
||||
)
|
||||
@@ -1374,7 +1445,11 @@ function main(): void {
|
||||
const plan = querySummary<BackfillPlan>(options.target, BACKFILL_PLAN_SQL)
|
||||
console.log(JSON.stringify({ mode: options.apply ? 'apply' : 'dry-run', before, plan }, null, 2))
|
||||
if (!options.apply) return
|
||||
const maxExpectedBuckets = Math.max(before.counterExpectedBuckets, before.signupExpectedBuckets)
|
||||
const maxExpectedBuckets = Math.max(
|
||||
before.counterExpectedBuckets,
|
||||
before.signupExpectedBuckets,
|
||||
before.trafficExpectedBuckets,
|
||||
)
|
||||
if (maxExpectedBuckets > MAX_BACKFILL_HOURS) {
|
||||
throw new Error(`admin_stats_backfill_range_too_large:${maxExpectedBuckets}`)
|
||||
}
|
||||
|
||||
@@ -89,6 +89,26 @@ describe('admin stats source integrity', () => {
|
||||
expect(rows).toEqual([{ events: 0, issuedAt: issuedAt.getTime() }])
|
||||
})
|
||||
|
||||
it('validates issued traffic from the earlier traffic-ledger boundary', async () => {
|
||||
const { db } = await createTestApp()
|
||||
const reports = createCloudTrafficReportRepo(db)
|
||||
await reports.ensureLedgerOpening(new Date('2026-07-21T10:05:00.000Z'))
|
||||
const opening = await ensureAdminStatsIntegrityOpening(db, new Date('2026-07-21T12:00:00.000Z'))
|
||||
const issuedAt = Date.parse('2026-07-21T11:30:00.000Z')
|
||||
await db.run(sql`
|
||||
INSERT INTO cloud_traffic_reports (
|
||||
id, org_id, period, source, source_id, event_id, bytes, status, issued_at, created_at, updated_at
|
||||
) VALUES (
|
||||
'traffic-before-global-invalid', 'org-1', '2026-07', 'unknown', 'matter-1',
|
||||
'traffic-before-global-invalid', 10, 'not_required', ${issuedAt}, ${issuedAt}, ${issuedAt}
|
||||
)
|
||||
`)
|
||||
|
||||
const integrity = await inspectAdminStatsSourceIntegrity(db, opening)
|
||||
|
||||
expect(integrity.invalidIssuedTrafficReports).toBe(1)
|
||||
})
|
||||
|
||||
it('rejects missing traffic reports without creating a duplicate event', async () => {
|
||||
const { db } = await createTestApp()
|
||||
const reports = createCloudTrafficReportRepo(db)
|
||||
|
||||
@@ -2,6 +2,7 @@ import { eq, sql } from 'drizzle-orm'
|
||||
import { auditEvents, systemOptions } from '../../db/schema'
|
||||
import { validDownloadTaskEventPredicate } from '../../domain/download-task-events'
|
||||
import type { Database } from '../../platform/interface'
|
||||
import { createCloudTrafficReportRepo, trafficLedgerExactFrom } from './cloud-traffic-report'
|
||||
|
||||
const OPENING_SOURCE_ID = 'v3-authoritative-sources'
|
||||
const OPENING_EVENT_ID = `audit:statistics_source_initialized:${OPENING_SOURCE_ID}`
|
||||
@@ -56,6 +57,8 @@ export async function inspectAdminStatsSourceIntegrity(
|
||||
if (!exactFrom) throw new Error('admin_stats_integrity_opening_missing')
|
||||
const exactFromMs = exactFrom.getTime()
|
||||
const exactFromSec = Math.floor(exactFromMs / 1000)
|
||||
const trafficOpening = await createCloudTrafficReportRepo(db).getLedgerOpening()
|
||||
const trafficExactFromMs = trafficOpening ? trafficLedgerExactFrom(trafficOpening).getTime() : exactFromMs
|
||||
const validTaskEvent = sql.raw(validDownloadTaskEventPredicate('task_event.value'))
|
||||
const rows = await db.all<Omit<AdminStatsSourceIntegrity, 'exactFrom'>>(sql`
|
||||
WITH billable_storage AS (
|
||||
@@ -144,7 +147,7 @@ export async function inspectAdminStatsSourceIntegrity(
|
||||
(SELECT count FROM invalid_task_events) AS invalidDownloadTaskEvents,
|
||||
(SELECT COUNT(*)
|
||||
FROM cloud_traffic_reports ctr
|
||||
WHERE ctr.issued_at >= ${exactFromMs}
|
||||
WHERE ctr.issued_at >= ${trafficExactFromMs}
|
||||
AND (
|
||||
ctr.status = 'reversed'
|
||||
OR ctr.bytes < 0
|
||||
|
||||
@@ -274,6 +274,7 @@ async function getDashboardOverviewStats(
|
||||
trafficLedgerAvailable,
|
||||
previousTrafficLedgerAvailable,
|
||||
counterDays,
|
||||
downloadCounterDays,
|
||||
signupDays,
|
||||
signupCoverage,
|
||||
previousSignupCoverage,
|
||||
@@ -297,6 +298,7 @@ async function getDashboardOverviewStats(
|
||||
trafficLedgerAvailableInRange(db, effective),
|
||||
trafficLedgerAvailableInRange(db, previous),
|
||||
reader.completeDayKeys('counters'),
|
||||
reader.completeDayKeys('counters', ADMIN_STATS_METRICS.transferDownloadIssued),
|
||||
reader.completeDayKeys('counters', ADMIN_STATS_METRICS.userSignup),
|
||||
reader.coverage('counters', ADMIN_STATS_METRICS.userSignup),
|
||||
previousReader.coverage('counters', ADMIN_STATS_METRICS.userSignup),
|
||||
@@ -320,7 +322,7 @@ async function getDashboardOverviewStats(
|
||||
storageUsedBytes: storageUsedByDay.get(date) ?? null,
|
||||
uploadBytes: counterDays.has(date) && !missingBytes?.upload ? (uploadByDay.get(date) ?? 0) : null,
|
||||
downloadBytes:
|
||||
counterDays.has(date) &&
|
||||
downloadCounterDays.has(date) &&
|
||||
trafficFirstExactDay !== null &&
|
||||
date >= trafficFirstExactDay &&
|
||||
!missingBytes?.download
|
||||
@@ -696,6 +698,7 @@ async function getDashboardTrafficStats(
|
||||
trafficLedgerAvailable,
|
||||
previousTrafficLedgerAvailable,
|
||||
counterDays,
|
||||
downloadCounterDays,
|
||||
trafficFirstExactDay,
|
||||
] = await Promise.all([
|
||||
getTrafficTotals(reader),
|
||||
@@ -719,6 +722,7 @@ async function getDashboardTrafficStats(
|
||||
trafficLedgerAvailableInRange(db, effective),
|
||||
trafficLedgerAvailableInRange(db, previous),
|
||||
reader.completeDayKeys('counters'),
|
||||
reader.completeDayKeys('counters', ADMIN_STATS_METRICS.transferDownloadIssued),
|
||||
trafficLedgerFirstExactDay(db),
|
||||
])
|
||||
const sourceRows = new Map<string, { name: string; bytes: number; requests: number }>()
|
||||
@@ -748,7 +752,7 @@ async function getDashboardTrafficStats(
|
||||
date,
|
||||
uploadBytes: counterDays.has(date) && !missingBytesByDay.get(date)?.upload ? (uploadByDay.get(date) ?? 0) : null,
|
||||
downloadBytes:
|
||||
counterDays.has(date) &&
|
||||
downloadCounterDays.has(date) &&
|
||||
trafficFirstExactDay !== null &&
|
||||
date >= trafficFirstExactDay &&
|
||||
!missingBytesByDay.get(date)?.download
|
||||
|
||||
@@ -327,6 +327,62 @@ describe('site stats routes', () => {
|
||||
expect(sharing.typeBreakdown).toEqual([])
|
||||
})
|
||||
|
||||
it('uses the traffic ledger boundary instead of the later global statistics boundary', async () => {
|
||||
const { app, db } = await createTestApp()
|
||||
const headers = await adminHeaders(app)
|
||||
await seedProLicense(db)
|
||||
const firstHour = Date.UTC(2026, 6, 1, 23)
|
||||
const secondHour = firstHour + 3_600_000
|
||||
const issuedAt = firstHour + 10 * 60_000
|
||||
await db.run(sql`
|
||||
INSERT INTO cloud_traffic_reports (
|
||||
id, org_id, period, source, source_id, event_id, bytes, status, issued_at, created_at, updated_at
|
||||
) VALUES
|
||||
('traffic_ledger_opening_v1', '', '2026-07', 'object_download', 'traffic_ledger_opening_v1',
|
||||
'traffic_ledger_opening_v1', 0, 'ledger_opening', NULL, ${firstHour - 3_600_000}, ${firstHour - 3_600_000}),
|
||||
('traffic-before-global-opening', 'org-traffic', '2026-07', 'object_download', 'file-traffic',
|
||||
'traffic-before-global-opening', 1024, 'not_required', ${issuedAt}, ${issuedAt}, ${issuedAt})
|
||||
`)
|
||||
await db.run(sql`
|
||||
INSERT INTO stats_rollups_hourly (
|
||||
id, bucket_start, org_id, metric_key, dimension_key, dimension_value,
|
||||
count, bytes, unique_count, metadata, updated_at
|
||||
) VALUES
|
||||
('traffic-marker-first', ${firstHour}, '', 'stats.rollup_run', 'metric_key', 'transfer.download_issued',
|
||||
1, 0, 0, '{"version":3,"scope":"counters","quality":"exact"}', ${firstHour + 3_600_000}),
|
||||
('traffic-marker-second', ${secondHour}, '', 'stats.rollup_run', 'metric_key', 'transfer.download_issued',
|
||||
1, 0, 0, '{"version":3,"scope":"counters","quality":"exact"}', ${secondHour + 3_600_000}),
|
||||
('global-marker-second', ${secondHour}, '', 'stats.rollup_run', '', '',
|
||||
1, 0, 0, '{"version":3,"scope":"counters","quality":"exact"}', ${secondHour + 3_600_000}),
|
||||
('traffic-result-first', ${firstHour}, 'org-traffic', 'transfer.download_issued', '', '',
|
||||
1, 1024, 0, '{"version":3,"scope":"counters","quality":"exact"}', ${firstHour + 3_600_000}),
|
||||
('traffic-source-first', ${firstHour}, 'org-traffic', 'transfer.download_issued', 'source', 'object_download',
|
||||
1, 1024, 0, '{"version":3,"scope":"counters","quality":"exact"}', ${firstHour + 3_600_000})
|
||||
`)
|
||||
|
||||
const res = await app.request(
|
||||
'/api/site/stats/traffic?from=2026-07-01T23%3A00%3A00.000Z&to=2026-07-02T00%3A59%3A59.999Z&timeZone=UTC',
|
||||
{ headers },
|
||||
)
|
||||
const body = (await res.json()) as {
|
||||
summary: { totalBytes: { value: number | null }; issuedDownloads: number | null }
|
||||
trafficTrend: Array<{
|
||||
date: string
|
||||
uploadBytes: number | null
|
||||
downloadBytes: number | null
|
||||
requests: number | null
|
||||
}>
|
||||
}
|
||||
|
||||
expect(res.status).toBe(200)
|
||||
expect(body.summary.totalBytes.value).toBe(1024)
|
||||
expect(body.summary.issuedDownloads).toBe(1)
|
||||
expect(body.trafficTrend).toEqual([
|
||||
{ date: '2026-07-01', uploadBytes: null, downloadBytes: 1024, requests: null },
|
||||
{ date: '2026-07-02', uploadBytes: 0, downloadBytes: 0, requests: 0 },
|
||||
])
|
||||
})
|
||||
|
||||
it('publishes exact current-hour snapshots without treating open counters as complete', async () => {
|
||||
const { db } = await createTestApp()
|
||||
const bucketStart = Date.UTC(2026, 6, 22, 10)
|
||||
|
||||
@@ -58,6 +58,8 @@ describe('admin stats backfill', () => {
|
||||
const now = new Date('2026-07-10T12:00:00.000Z')
|
||||
const historyStartMs = Date.parse('2026-04-01T00:10:00.000Z')
|
||||
const preExactSignupMs = Date.parse('2026-03-31T22:10:00.000Z')
|
||||
const trafficOpeningMs = Date.parse('2026-03-31T20:10:00.000Z')
|
||||
const trafficFirstHour = Math.ceil(trafficOpeningMs / 3_600_000) * 3_600_000
|
||||
const eventMs = Date.parse('2026-07-10T09:10:00.000Z')
|
||||
const eventHourMs = Date.parse('2026-07-10T09:00:00.000Z')
|
||||
const sessionCreatedMs = Date.parse('2026-07-10T08:00:00.000Z')
|
||||
@@ -73,6 +75,7 @@ describe('admin stats backfill', () => {
|
||||
const expectedBuckets = (latestClosedHour - firstExactHour) / 3_600_000 + 1
|
||||
const signupFirstHour = Math.floor(preExactSignupMs / 3_600_000) * 3_600_000
|
||||
const expectedSignupBuckets = (latestClosedHour - signupFirstHour) / 3_600_000 + 1
|
||||
const expectedTrafficBuckets = (latestClosedHour - trafficFirstHour) / 3_600_000 + 1
|
||||
db.exec(`
|
||||
CREATE TABLE user (id TEXT PRIMARY KEY, created_at INTEGER NOT NULL DEFAULT 0, last_active_at INTEGER);
|
||||
CREATE TABLE account (id TEXT PRIMARY KEY, user_id TEXT NOT NULL, provider_id TEXT NOT NULL, created_at INTEGER NOT NULL);
|
||||
@@ -183,6 +186,11 @@ describe('admin stats backfill', () => {
|
||||
id, org_id, period, source, source_id, event_id, bytes, storage_id, unit_bytes, credits_per_unit,
|
||||
status, error, attempt_count, next_retry_at, issued_at, created_at, updated_at
|
||||
) VALUES
|
||||
('traffic_ledger_opening_v1', '', '2026-03', 'object_download', 'traffic_ledger_opening_v1',
|
||||
'traffic_ledger_opening_v1', 0, NULL, NULL, NULL, 'ledger_opening', NULL, 0, NULL, NULL,
|
||||
${trafficOpeningMs}, ${trafficOpeningMs}),
|
||||
('r0', 'o1', '2026-03', 'object_download', 'f1', 'traffic-0', 64, NULL, NULL, NULL,
|
||||
'not_required', NULL, 0, NULL, ${preExactSignupMs}, ${preExactSignupMs}, ${preExactSignupMs}),
|
||||
('r1', 'o1', '2026-07', 'direct_share', 's1', 'traffic-1', 512, NULL, NULL, NULL, 'reported', NULL, 0, NULL, NULL, ${eventMs}, ${eventMs}),
|
||||
('r2', 'o1', '2026-07', 'image_hosting', 'img1', 'traffic-2', 128, NULL, NULL, NULL, 'reported', NULL, 0, NULL, NULL, ${eventMs}, ${eventMs}),
|
||||
('r3', 'o1', '2026-07', 'object_download', 'f1', 'traffic-3', 512, NULL, NULL, NULL, 'blocked', 'quota_exceeded', 0, NULL, NULL, ${eventMs}, ${eventMs});
|
||||
@@ -255,7 +263,14 @@ describe('admin stats backfill', () => {
|
||||
...buildAdminStatsCounterRowsSqlStatements({
|
||||
fromMs: firstExactHour,
|
||||
toMs: currentHourMs,
|
||||
metrics: ADMIN_STATS_FACT_COUNTER_METRICS.filter((metric) => metric !== 'user.signup'),
|
||||
metrics: ADMIN_STATS_FACT_COUNTER_METRICS.filter(
|
||||
(metric) => metric !== 'user.signup' && metric !== 'transfer.download_issued',
|
||||
),
|
||||
}).flatMap((statement) => db.prepare(statement).all()),
|
||||
...buildAdminStatsCounterRowsSqlStatements({
|
||||
fromMs: trafficFirstHour,
|
||||
toMs: currentHourMs,
|
||||
metrics: ['transfer.download_issued'],
|
||||
}).flatMap((statement) => db.prepare(statement).all()),
|
||||
...buildAdminStatsCounterRowsSqlStatements({
|
||||
fromMs: signupFirstHour,
|
||||
@@ -307,6 +322,9 @@ describe('admin stats backfill', () => {
|
||||
signupExpectedBuckets: expectedSignupBuckets,
|
||||
signupCompletedBuckets: expectedSignupBuckets,
|
||||
signupMissingBuckets: 0,
|
||||
trafficExpectedBuckets: expectedTrafficBuckets,
|
||||
trafficCompletedBuckets: expectedTrafficBuckets,
|
||||
trafficMissingBuckets: 0,
|
||||
openCounterMarkers: 0,
|
||||
requiredDimensionMismatchGroups: 0,
|
||||
userSignupProviderMismatchGroups: 0,
|
||||
@@ -314,6 +332,10 @@ describe('admin stats backfill', () => {
|
||||
lowerBoundRollups: 0,
|
||||
rawUploadAttempts: 1,
|
||||
rollupUploadAttempts: 1,
|
||||
rawDownloadEvents: 2,
|
||||
rollupDownloadEvents: 2,
|
||||
rawDownloadBytes: 576,
|
||||
rollupDownloadBytes: 576,
|
||||
rawUserSignups: 3,
|
||||
rollupUserSignups: 3,
|
||||
rawSharesCreated: 1,
|
||||
|
||||
Reference in New Issue
Block a user