diff --git a/server/src/internal/balances/utils/sync/logs/logSyncItem.ts b/server/src/internal/balances/utils/sync/logs/logSyncItem.ts new file mode 100644 index 000000000..fee87754f --- /dev/null +++ b/server/src/internal/balances/utils/sync/logs/logSyncItem.ts @@ -0,0 +1,62 @@ +import type { AutumnContext } from "@/honoUtils/HonoEnv"; +import { addToExtraLogs } from "@/utils/logging/addToExtraLogs"; +import type { RolloverSyncEntry, SyncEntry } from "../syncItemV4"; + +type SyncItemResult = + | { + kind: "synced"; + entries: SyncEntry[]; + rolloverEntries: RolloverSyncEntry[]; + updateCount: number; + rolloverUpdateCount: number; + } + | { kind: "skipped"; reason: "cache_miss" | "no_entries"; feature?: string }; + +const formatEntry = (entry: SyncEntry): string => { + const entitiesStr = + entry.entities && Object.keys(entry.entities).length > 0 + ? `, entities=${Object.keys(entry.entities).length}` + : ""; + return `${entry.feature_id} (${entry.customer_entitlement_id}): bal=${entry.balance}, adj=${entry.adjustment}${entitiesStr}`; +}; + +const formatRollover = (entry: RolloverSyncEntry): string => { + const entitiesStr = + entry.entities && Object.keys(entry.entities).length > 0 + ? `, entities=${Object.keys(entry.entities).length}` + : ""; + return `${entry.rollover_id}: bal=${entry.balance}, usage=${entry.usage}${entitiesStr}`; +}; + +export const logSyncItem = ({ + ctx, + result, +}: { + ctx: AutumnContext; + result: SyncItemResult; +}) => { + if (result.kind === "skipped") { + addToExtraLogs({ + ctx, + extras: { + syncItemV4: { + skipped: result.feature + ? `${result.reason} (feature=${result.feature})` + : result.reason, + }, + }, + }); + return; + } + + addToExtraLogs({ + ctx, + extras: { + syncItemV4: { + entries: result.entries.map(formatEntry), + rollovers: result.rolloverEntries.map(formatRollover), + updated: `${result.updateCount} cus_ents, ${result.rolloverUpdateCount} rollovers`, + }, + }, + }); +}; diff --git a/server/src/internal/balances/utils/sync/syncItemV4.ts b/server/src/internal/balances/utils/sync/syncItemV4.ts index 7b758b550..d17670c30 100644 --- a/server/src/internal/balances/utils/sync/syncItemV4.ts +++ b/server/src/internal/balances/utils/sync/syncItemV4.ts @@ -10,6 +10,7 @@ import type { AutumnContext } from "@/honoUtils/HonoEnv.js"; import { getCachedFeatureBalance } from "@/internal/customers/cache/fullSubject/balances/getCachedFeatureBalances.js"; import { deleteCachedFullCustomer } from "@/internal/customers/cusUtils/fullCustomerCacheUtils/deleteCachedFullCustomer.js"; import { globalRefreshEntityAggregateBatchingManager } from "../refreshEntityAggregate/RefreshEntityAggregateBatchingManager"; +import { logSyncItem } from "./logs/logSyncItem"; const SYNC_CONFLICT_CODES = { ResetAtMismatch: "RESET_AT_MISMATCH", @@ -69,7 +70,7 @@ interface SyncItemV4 { modifiedCusEntIdsByFeatureId: Record; } -interface SyncEntry { +export interface SyncEntry { customer_entitlement_id: string; feature_id: string; balance: number; @@ -80,7 +81,7 @@ interface SyncEntry { cache_version: number | null; } -interface RolloverSyncEntry { +export interface RolloverSyncEntry { rollover_id: string; balance: number; usage: number; @@ -104,26 +105,6 @@ const subjectBalanceToSyncEntry = ({ cache_version: subjectBalance.cache_version ?? 0, }); -const formatSyncEntry = ({ entry }: { entry: SyncEntry }): string => { - const hasEntities = entry.entities && Object.keys(entry.entities).length > 0; - const entitiesStr = hasEntities - ? `, entities= ${Object.keys(entry.entities!).length}` - : ""; - return `${entry.feature_id} (${entry.customer_entitlement_id}): bal= ${entry.balance}, adj= ${entry.adjustment}${entitiesStr}`; -}; - -const formatRolloverSyncEntry = ({ - entry, -}: { - entry: RolloverSyncEntry; -}): string => { - const hasEntities = entry.entities && Object.keys(entry.entities).length > 0; - const entitiesStr = hasEntities - ? `, entities= ${Object.keys(entry.entities!).length}` - : ""; - return `rollover ${entry.rollover_id}: bal= ${entry.balance}, usage= ${entry.usage}${entitiesStr}`; -}; - /** Sync cached subject balances to Postgres using targeted hash reads. */ export const syncItemV4 = async ({ ctx, @@ -134,7 +115,7 @@ export const syncItemV4 = async ({ }): Promise => { const { customerId, entityId, rolloverIds, modifiedCusEntIdsByFeatureId } = payload; - const { db, logger } = ctx; + const { db } = ctx; // Read targeted balance hashes const allSubjectBalances: SubjectBalance[] = []; @@ -150,9 +131,14 @@ export const syncItemV4 = async ({ }); if (outcome.kind !== "ok") { - logger.info( - `[SYNC V4] (${customerId}) Cache ${outcome.kind} for feature=${featureId}, skipping`, - ); + logSyncItem({ + ctx, + result: { + kind: "skipped", + reason: "cache_miss", + feature: featureId, + }, + }); return; } @@ -184,19 +170,10 @@ export const syncItemV4 = async ({ } if (entries.length === 0 && rolloverEntries.length === 0) { - logger.info(`[SYNC V4] (${customerId}) No entries to sync`); + logSyncItem({ ctx, result: { kind: "skipped", reason: "no_entries" } }); return; } - for (const entry of entries) { - logger.info(`[SYNC V4] (${customerId}) ${formatSyncEntry({ entry })}`); - } - for (const entry of rolloverEntries) { - logger.info( - `[SYNC V4] (${customerId}) ${formatRolloverSyncEntry({ entry })}`, - ); - } - const { data: result, error } = await tryCatch( db.execute( sql`SELECT * FROM sync_balances_v2(${JSON.stringify({ @@ -230,9 +207,16 @@ export const syncItemV4 = async ({ ? Object.keys(syncResult.rollover_updates).length : 0; - logger.info( - `[SYNC V4] (${customerId}) Done: ${updateCount} cus_ents, ${rolloverUpdateCount} rollovers updated`, - ); + logSyncItem({ + ctx, + result: { + kind: "synced", + entries, + rolloverEntries, + updateCount, + rolloverUpdateCount, + }, + }); const hasEntityLevel = allSubjectBalances.some( (subjectBalance) => subjectBalance.isEntityLevel,