From 29ea681749f9ff4ce9f9667a31947c321a6471ea Mon Sep 17 00:00:00 2001 From: John Yeo Date: Mon, 5 Jan 2026 14:24:57 +0000 Subject: [PATCH] chore: update verify cache workflow to only run for paid products --- server/src/external/sentry/sentryUtils.ts | 3 +++ .../createStripePrice/createStripeInArrear.ts | 3 ++- .../src/honoMiddlewares/rateLimitMiddleware.ts | 6 ++++++ server/src/init.ts | 2 +- .../add-product/createFullCusProduct.ts | 2 +- .../add-product/createOneTimeCusProduct.ts | 2 +- .../checkForMisingBalance.ts | 6 ++++-- .../queueVerifyCacheConsistencyWorkflow.ts | 17 +++++++++++++---- .../verifyCacheConsistencyWorkflow.ts | 1 + server/tests/attach/upgrade/upgrade2.test.ts | 6 +++--- 10 files changed, 35 insertions(+), 13 deletions(-) diff --git a/server/src/external/sentry/sentryUtils.ts b/server/src/external/sentry/sentryUtils.ts index 032bd40fa..470282e84 100644 --- a/server/src/external/sentry/sentryUtils.ts +++ b/server/src/external/sentry/sentryUtils.ts @@ -32,12 +32,14 @@ export const getSentryTags = ({ messageId, path, method, + alert = false, }: { ctx: AutumnContext; customerId?: string; messageId?: string; path?: string; method?: string; + alert?: boolean; }) => { if (!ctx) return; return { @@ -52,5 +54,6 @@ export const getSentryTags = ({ method: method, email: ctx.user?.email, + alert: alert ? "true" : "false", }; }; diff --git a/server/src/external/stripe/createStripePrice/createStripeInArrear.ts b/server/src/external/stripe/createStripePrice/createStripeInArrear.ts index d45d902c9..01ff36959 100644 --- a/server/src/external/stripe/createStripePrice/createStripeInArrear.ts +++ b/server/src/external/stripe/createStripePrice/createStripeInArrear.ts @@ -32,9 +32,10 @@ export const searchStripeMeter = async ({ }) => { const allStripeMeters = []; let hasMore = true; - let startingAfter; + let startingAfter: string | undefined; const start = performance.now(); + // Get max 200 meters while (hasMore) { const response: any = await stripeCli.billing.meters.list({ limit: 100, diff --git a/server/src/honoMiddlewares/rateLimitMiddleware.ts b/server/src/honoMiddlewares/rateLimitMiddleware.ts index d4189143d..1d869a554 100644 --- a/server/src/honoMiddlewares/rateLimitMiddleware.ts +++ b/server/src/honoMiddlewares/rateLimitMiddleware.ts @@ -82,6 +82,12 @@ export const rateLimitMiddleware = async (c: Context, next: Next) => { const ctx = c.get("ctx"); try { + if ( + process.env.NODE_ENV === "development" && + ctx.org?.id === process.env.TESTS_ORG_ID + ) { + return await next(); + } // 1. Determine rate limit type based on endpoint const rateLimitType = getRateLimitType(c); diff --git a/server/src/init.ts b/server/src/init.ts index 11fd98743..5dc904bc1 100644 --- a/server/src/init.ts +++ b/server/src/init.ts @@ -1,9 +1,9 @@ // Suppress BullMQ eviction policy warnings BEFORE any imports // Skip OpenTelemetry instrumentation in development for faster startup +await import("./sentry.js"); if (process.env.NODE_ENV !== "development") { await import("./instrumentation.js"); - await import("./sentry.js"); } import cluster from "node:cluster"; diff --git a/server/src/internal/customers/add-product/createFullCusProduct.ts b/server/src/internal/customers/add-product/createFullCusProduct.ts index 4492c70dc..00bbb6f09 100644 --- a/server/src/internal/customers/add-product/createFullCusProduct.ts +++ b/server/src/internal/customers/add-product/createFullCusProduct.ts @@ -541,7 +541,7 @@ export const createFullCusProduct = async ({ } await queueVerifyCacheConsistencyWorkflow({ - newCustomerProductId: cusProdId, + newCustomerProduct: fullCusProduct, previousFullCustomer: attachParams.customer as FullCustomer, logger, source: "createFullCusProduct", diff --git a/server/src/internal/customers/add-product/createOneTimeCusProduct.ts b/server/src/internal/customers/add-product/createOneTimeCusProduct.ts index e2471fd82..7764db384 100644 --- a/server/src/internal/customers/add-product/createOneTimeCusProduct.ts +++ b/server/src/internal/customers/add-product/createOneTimeCusProduct.ts @@ -192,7 +192,7 @@ export const updateOneTimeCusProduct = async ({ }); await queueVerifyCacheConsistencyWorkflow({ - newCustomerProductId: existingCusProduct.id, + newCustomerProduct: existingCusProduct, previousFullCustomer: attachParams.customer as FullCustomer, logger, source: "updateOneTimeCusProduct", diff --git a/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/checkForMisingBalance.ts b/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/checkForMisingBalance.ts index c864d1496..8578145af 100644 --- a/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/checkForMisingBalance.ts +++ b/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/checkForMisingBalance.ts @@ -94,12 +94,14 @@ export const checkForMisingBalance = async ({ const threshold = grantedBalanceIncrease.mul(0.995); if (grantedBalanceIncrease.gt(0) && usageIncrease.gte(threshold)) { - const errMessage = `[RACE CONDITION DETECTED] Usage increase (${usageIncrease}), granted balance increase (${grantedBalanceIncrease.toNumber()}), feature (${feature.name}), customer (${fullCustomer.id})`; + const errMessage = `[RACE CONDITION] Usage increase (${usageIncrease}), granted balance increase (${grantedBalanceIncrease.toNumber()}), feature (${feature.name}), customer (${fullCustomer.id})`; - Sentry.captureException(new Error(errMessage), { + console.log("CAPTURING SENTRY EXCEPTION"); + Sentry.captureException(errMessage, { tags: getSentryTags({ ctx, customerId: fullCustomer.id || "", + alert: true, }), }); diff --git a/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/queueVerifyCacheConsistencyWorkflow.ts b/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/queueVerifyCacheConsistencyWorkflow.ts index bb6df3ac8..78def832c 100644 --- a/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/queueVerifyCacheConsistencyWorkflow.ts +++ b/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/queueVerifyCacheConsistencyWorkflow.ts @@ -1,15 +1,20 @@ -import type { FullCustomer } from "../../../../../shared"; +import { + cusProductToPrices, + type FullCusProduct, + type FullCustomer, + isFreeProduct, +} from "@autumn/shared"; import type { Logger } from "../../../external/logtail/logtailUtils"; import { JobName } from "../../JobName"; import { runHatchetWorkflow } from "../../queueUtils"; export const queueVerifyCacheConsistencyWorkflow = async ({ - newCustomerProductId, + newCustomerProduct, previousFullCustomer, logger, source, }: { - newCustomerProductId: string; + newCustomerProduct: FullCusProduct; previousFullCustomer: FullCustomer; logger: Logger; source: string; @@ -18,13 +23,17 @@ export const queueVerifyCacheConsistencyWorkflow = async ({ `[${source}] Scheduling verify cache workflow for customer ${previousFullCustomer.id || previousFullCustomer.internal_id}`, ); try { + // 1. Check if new customer product is not free + const newPrices = cusProductToPrices({ cusProduct: newCustomerProduct }); + if (isFreeProduct({ prices: newPrices })) return; + await runHatchetWorkflow({ workflowName: JobName.VerifyCacheConsistency, payload: { orgId: previousFullCustomer.org_id, env: previousFullCustomer.env, customerId: previousFullCustomer.id || previousFullCustomer.internal_id, - newCustomerProductId, + newCustomerProductId: newCustomerProduct.id, source, previousFullCustomer: JSON.stringify(previousFullCustomer), // is there a better approach to this...? }, diff --git a/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/verifyCacheConsistencyWorkflow.ts b/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/verifyCacheConsistencyWorkflow.ts index 43ad87830..3dcf0ec14 100644 --- a/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/verifyCacheConsistencyWorkflow.ts +++ b/server/src/queue/hatchetWorkflows/verifyCacheConsistencyWorkflow/verifyCacheConsistencyWorkflow.ts @@ -129,6 +129,7 @@ verifyCacheConsistencyWorkflow?.task({ tags: getSentryTags({ ctx: autumnContext, customerId, + alert: true, }), }); } diff --git a/server/tests/attach/upgrade/upgrade2.test.ts b/server/tests/attach/upgrade/upgrade2.test.ts index 42133c73b..899f18b65 100644 --- a/server/tests/attach/upgrade/upgrade2.test.ts +++ b/server/tests/attach/upgrade/upgrade2.test.ts @@ -1,12 +1,12 @@ import { beforeAll, describe, test } from "bun:test"; import { type AppEnv, LegacyVersion, type Organization } from "@autumn/shared"; -import chalk from "chalk"; -import { addWeeks } from "date-fns"; -import type Stripe from "stripe"; import { TestFeature } from "@tests/setup/v2Features.js"; import { attachAndExpectCorrect } from "@tests/utils/expectUtils/expectAttach.js"; import { advanceTestClock } from "@tests/utils/stripeUtils.js"; import ctx from "@tests/utils/testInitUtils/createTestContext.js"; +import chalk from "chalk"; +import { addWeeks } from "date-fns"; +import type Stripe from "stripe"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js";