clear entity cache on usage limit update
This commit is contained in:
@@ -50,6 +50,34 @@ for field_name, field_value in pairs(updates) do
|
||||
table.insert(updated_fields, field_name)
|
||||
end
|
||||
|
||||
if updates.usage_limits ~= nil then
|
||||
local seen_feature_ids = {}
|
||||
local usage_window_feature_ids = {}
|
||||
|
||||
local function append_usage_limit_feature_ids(usage_limits)
|
||||
if type(usage_limits) ~= 'table' then
|
||||
return
|
||||
end
|
||||
|
||||
for _, usage_limit in ipairs(usage_limits) do
|
||||
if type(usage_limit) == 'table' and usage_limit.feature_id ~= nil then
|
||||
local feature_id = usage_limit.feature_id
|
||||
if seen_feature_ids[feature_id] == nil then
|
||||
seen_feature_ids[feature_id] = true
|
||||
table.insert(usage_window_feature_ids, feature_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if cached.customer ~= nil then
|
||||
append_usage_limit_feature_ids(cached.customer.usage_limits)
|
||||
end
|
||||
append_usage_limit_feature_ids(cached.entity.usage_limits)
|
||||
|
||||
cached.usageWindowFeatureIds = usage_window_feature_ids
|
||||
end
|
||||
|
||||
redis.call("SET", subject_key, cjson.encode(cached), "EX", cache_ttl)
|
||||
|
||||
return cjson.encode({ success = true, updated_fields = updated_fields })
|
||||
|
||||
@@ -278,6 +278,8 @@ export const executeRedisDeductionV2 = async ({
|
||||
)
|
||||
? resultJson.usage_window_mutations
|
||||
: [];
|
||||
const usageWindowsByFeatureId =
|
||||
resultJson.usage_windows_by_feature_id ?? {};
|
||||
const modifiedCustomerEntitlementIds = Array.isArray(
|
||||
resultJson.modified_customer_entitlement_ids,
|
||||
)
|
||||
@@ -301,7 +303,7 @@ export const executeRedisDeductionV2 = async ({
|
||||
// Typed handoff for the PG mirror; empty arrays kept (prune-to-empty
|
||||
// must still full-replace).
|
||||
for (const [featureId, usageWindows] of Object.entries(
|
||||
resultJson.usage_windows_by_feature_id ?? {},
|
||||
usageWindowsByFeatureId,
|
||||
)) {
|
||||
allUsageWindowUpdates[featureId] = {
|
||||
internal_customer_id: fullSubject.internalCustomerId,
|
||||
|
||||
@@ -20,7 +20,10 @@ export const updateCachedEntityData = async ({
|
||||
customerId: string;
|
||||
entityId: string;
|
||||
updates: Partial<
|
||||
Pick<Entity, "spend_limits" | "usage_alerts" | "overage_allowed">
|
||||
Pick<
|
||||
Entity,
|
||||
"spend_limits" | "usage_limits" | "usage_alerts" | "overage_allowed"
|
||||
>
|
||||
>;
|
||||
}): Promise<void> => {
|
||||
if (Object.keys(updates).length === 0) return;
|
||||
|
||||
@@ -131,6 +131,71 @@ test.concurrent(
|
||||
},
|
||||
);
|
||||
|
||||
test.concurrent(
|
||||
`${chalk.yellowBright("ent-uw-enforce1b: entities.update usage_limits patches a pre-existing cached subject")}`,
|
||||
async () => {
|
||||
const perEntityProduct = products.base({
|
||||
id: "ent-uw-enforce-cached-update",
|
||||
items: [
|
||||
items.monthlyMessages({
|
||||
includedUsage: 100,
|
||||
entityFeatureId: TestFeature.Users,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const customerId = "ent-uw-enforce-cached-1";
|
||||
const { entities } = await initScenario({
|
||||
customerId,
|
||||
setup: [
|
||||
s.customer({ testClock: false }),
|
||||
s.products({ list: [perEntityProduct] }),
|
||||
s.entities({ count: 1, featureId: TestFeature.Users }),
|
||||
],
|
||||
actions: [s.billing.attach({ productId: perEntityProduct.id })],
|
||||
});
|
||||
|
||||
await autumnV2_3.check({
|
||||
customer_id: customerId,
|
||||
entity_id: entities[0].id,
|
||||
feature_id: TestFeature.Messages,
|
||||
});
|
||||
|
||||
await setEntityUsageLimit({
|
||||
autumn: autumnV2_3,
|
||||
customerId,
|
||||
entityId: entities[0].id,
|
||||
featureId: TestFeature.Messages,
|
||||
limit: 5,
|
||||
});
|
||||
|
||||
await autumnV2_3.track({
|
||||
customer_id: customerId,
|
||||
entity_id: entities[0].id,
|
||||
feature_id: TestFeature.Messages,
|
||||
value: 7,
|
||||
});
|
||||
|
||||
await expectEntityFeatureBalance({
|
||||
autumn: autumnV2_3,
|
||||
customerId,
|
||||
entityId: entities[0].id,
|
||||
featureId: TestFeature.Messages,
|
||||
granted: 100,
|
||||
remaining: 95,
|
||||
usage: 5,
|
||||
});
|
||||
await expectEntityUsageLimit({
|
||||
autumn: autumnV2_3,
|
||||
customerId,
|
||||
entityId: entities[0].id,
|
||||
featureId: TestFeature.Messages,
|
||||
usage: 5,
|
||||
limit: 5,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.concurrent(
|
||||
`${chalk.yellowBright("ent-uw-enforce2: two entities with different caps stay isolated while customer balance aggregates")}`,
|
||||
async () => {
|
||||
|
||||
Reference in New Issue
Block a user