Files
cfw-autumn/server/src/internal/customers/cache/fullSubject/actions/setCachedFullSubject/setCachedFullSubject.ts
2026-04-19 15:21:44 +01:00

84 lines
2.5 KiB
TypeScript

import type { NormalizedFullSubject } from "@autumn/shared";
import { redisV2 } from "@/external/redis/initRedisV2.js";
import type { AutumnContext } from "@/honoUtils/HonoEnv.js";
import { tryRedisWrite } from "@/utils/cacheUtils/cacheUtils.js";
import { buildFullSubjectKey } from "../../builders/buildFullSubjectKey.js";
import { buildFullSubjectViewEpochKey } from "../../builders/buildFullSubjectViewEpochKey.js";
import { FULL_SUBJECT_CACHE_TTL_SECONDS } from "../../config/fullSubjectCacheConfig.js";
import { normalizedToCachedFullSubject } from "../../fullSubjectCacheModel.js";
import type { SetCachedFullSubjectResult } from "./fullSubjectWriteTypes.js";
import { buildSharedBalanceWrites } from "./setSharedFullSubjectBalances.js";
export type { SetCachedFullSubjectResult } from "./fullSubjectWriteTypes.js";
export const setCachedFullSubject = async ({
ctx,
normalized,
fetchedSubjectViewEpoch,
}: {
ctx: AutumnContext;
normalized: NormalizedFullSubject;
fetchedSubjectViewEpoch: number;
}): Promise<SetCachedFullSubjectResult> => {
const { logger, org, env } = ctx;
const { customerId, entityId } = normalized;
const cached = normalizedToCachedFullSubject({
normalized,
subjectViewEpoch: fetchedSubjectViewEpoch,
});
const subjectKey = buildFullSubjectKey({
orgId: org.id,
env,
customerId,
entityId,
});
const epochKey = buildFullSubjectViewEpochKey({
orgId: org.id,
env,
customerId,
});
const balanceWrites = buildSharedBalanceWrites({
orgId: org.id,
env,
customerId,
customerEntitlements: normalized.customer_entitlements,
aggregatedCustomerEntitlements:
normalized.entity_aggregations?.aggregated_customer_entitlements ?? [],
});
const keys: string[] = [subjectKey, epochKey];
for (const { balanceKey } of balanceWrites) {
keys.push(balanceKey);
}
const argv: string[] = [
String(fetchedSubjectViewEpoch),
String(FULL_SUBJECT_CACHE_TTL_SECONDS),
JSON.stringify(cached),
String(balanceWrites.length),
];
for (const { fields } of balanceWrites) {
const fieldEntries = Object.entries(fields);
argv.push(String(fieldEntries.length));
for (const [fieldName, fieldValue] of fieldEntries) {
argv.push(fieldName, fieldValue);
}
}
const result = await tryRedisWrite(
() => redisV2.setCachedFullSubject(keys.length, ...keys, ...argv),
redisV2,
);
const subjectLabel = entityId ? `${customerId}:${entityId}` : customerId;
logger.info(
`[setCachedFullSubject] ${subjectLabel}: ${result ?? "FAILED"}, balances=${cached.meteredFeatures.length}`,
);
return result ?? "FAILED";
};