Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion apps/webapp/app/env.server.ts

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Self-hosted configuration fix lacks release note

Self-hosted operators can now configure 15-second metrics buckets, but this fix has no new server release note. The release-note guidance calls for one when users would notice a server-only fix.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,6 @@ const EnvironmentSchema = z
.number()
.int()
.positive()
.multipleOf(10)
.refine((seconds) => 60 % seconds === 0 || seconds % 60 === 0, {
message: "must divide or be a multiple of 60 so buckets align to minute boundaries",
})
Expand Down
38 changes: 38 additions & 0 deletions apps/webapp/test/env.server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,41 @@ describe("webapp environment secrets", () => {
expect(env.GITHUB_APP_CLIENT_SECRET).toBe("client-secret");
});
});

describe("API_RATE_LIMIT_METRICS_BUCKET_SECONDS", () => {
afterEach(() => {
process.env = originalEnv;
vi.resetModules();
});

it("defaults to 10 when unset", async () => {
process.env = { ...requiredEnv };
delete process.env.API_RATE_LIMIT_METRICS_BUCKET_SECONDS;

const { env } = await import("../app/env.server");

expect(env.API_RATE_LIMIT_METRICS_BUCKET_SECONDS).toBe(10);
});

it.each(["10", "15", "20", "30", "60", "120"])(
"accepts valid divisor or multiple of 60: %s",
async (value) => {
process.env = { ...requiredEnv, API_RATE_LIMIT_METRICS_BUCKET_SECONDS: value };

const { env } = await import("../app/env.server");

expect(env.API_RATE_LIMIT_METRICS_BUCKET_SECONDS).toBe(Number(value));
}
);

it.each(["7", "0", "-10", "15.5", "not-a-number"])(
"rejects invalid bucket seconds: %s",
async (value) => {
process.env = { ...requiredEnv, API_RATE_LIMIT_METRICS_BUCKET_SECONDS: value };

await expect(import("../app/env.server")).rejects.toThrow(
"API_RATE_LIMIT_METRICS_BUCKET_SECONDS"
);
}
);
});
2 changes: 1 addition & 1 deletion docs/self-hosting/env/webapp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ mode: "wide"
| `API_RATE_LIMIT_JWT_WINDOW` | No | 1m | API rate limit JWT window. |
| `API_RATE_LIMIT_JWT_TOKENS` | No | 60 | API rate limit JWT tokens. |
| `API_RATE_LIMIT_METRICS_ENABLED` | No | 0 | Record API rate limit usage into the `metrics` table: `1` for everyone, `allowlist` for flagged organizations. |
| `API_RATE_LIMIT_METRICS_BUCKET_SECONDS` | No | 10 | Aggregation bucket width for API rate limit metrics: 10, 20, 30 or a multiple of 60. |
| `API_RATE_LIMIT_METRICS_BUCKET_SECONDS` | No | 10 | Aggregation bucket width for API rate limit metrics: any divisor of 60 (e.g. 10, 15, 20, 30) or a multiple of 60. |
| `API_RATE_LIMIT_METRICS_FLUSH_INTERVAL_MS` | No | 10000 | How often aggregated API rate limit metrics are flushed to ClickHouse. |
| `API_RATE_LIMIT_METRICS_MAX_ENTRIES` | No | 10000 | Cap on distinct (environment, bucket) entries held between flushes. |
| `API_RATE_LIMIT_METRICS_WAIT_FOR_ASYNC_INSERT` | No | 0 | Wait for ClickHouse to write each async insert of API rate limit metrics, surfacing write failures. |
Expand Down