Skip to main content 홈 크리에이터 jeremylongshore claude-code-plugins-plus-skills sentry-load-scale
sentry-load-scale Scale Sentry for high-traffic applications handling millions of events per day.
Use when optimizing SDK performance at high volume, implementing adaptive sampling,
managing quotas and costs at scale, or deploying Sentry across multi-region infrastructure.
Trigger with phrases like "sentry high traffic", "scale sentry", "sentry millions events",
"sentry high volume", "sentry quota management", "sentry load test".
설치로 이동 Skills Marketplace 커뮤니티가 만든 AI 스킬을 발견하고 탐색하세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill sentry-load-scale명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Zip 다운로드 다운로드 중... 이 저장소의 다른 Skills Implement user sign-up and sign-in flows with Clerk.
Use when building authentication UI, customizing sign-in experience,
or implementing OAuth social login.
Trigger with phrases like "clerk sign-in", "clerk sign-up",
"clerk login flow", "clerk OAuth", "clerk social login".
Implement session management and middleware with Clerk.
Use when managing user sessions, configuring route protection,
or implementing token refresh and custom JWT templates.
Trigger with phrases like "clerk session", "clerk middleware",
"clerk route protection", "clerk token", "clerk JWT".
Configure enterprise SSO, role-based access control, and organization management.
Use when implementing SSO integration, configuring role-based permissions,
or setting up organization-level controls.
Trigger with phrases like "clerk SSO", "clerk RBAC",
"clerk enterprise", "clerk roles", "clerk permissions", "clerk organizations".
jeremylongshore
jeremylongshore/claude-code-plugins-plus-skills
GitHub 저장소 열기 name sentry-load-scale description Scale Sentry for high-traffic applications handling millions of events per day.
Use when optimizing SDK performance at high volume, implementing adaptive sampling,
managing quotas and costs at scale, or deploying Sentry across multi-region infrastructure.
Trigger with phrases like "sentry high traffic", "scale sentry", "sentry millions events",
"sentry high volume", "sentry quota management", "sentry load test".
allowed-tools Read, Write, Edit, Grep, Bash(node:*), Bash(npx:*), Bash(k6:*) version 1.51.0 license MIT author Jeremy Longshore <jeremy@intentsolutions.io> tags ["saas","sentry","performance","scaling","high-traffic","enterprise"] compatibility Designed for Claude Code, also compatible with Codex and OpenClaw
Sentry Load & Scale
Overview
Configure Sentry for applications processing 1M+ requests/day without sacrificing error visibility, burning through quota, or adding measurable SDK overhead. Covers adaptive sampling, connection pooling, multi-region tagging, quota management, SDK benchmarking, batch submission, load testing, and self-hosted deployment considerations.
Prerequisites
Application handling sustained high traffic (>10K requests/min or >1M events/day)
Sentry organization with quota and billing access (Settings > Subscription)
@sentry/node v8+ installed (npm ls @sentry/node)
Performance baseline established (p50/p95/p99 latency without Sentry)
Event volume estimates calculated per category (errors, transactions, replays, attachments)
Instructions
Step 1 — Implement Adaptive Sampling
Static tracesSampleRate wastes quota at scale because it treats a health check the same as a checkout. Replace it with a traffic-aware tracesSampler that adjusts rates based on endpoint criticality and current load.
Traffic-aware tracesSampler:
import * as Sentry from '@sentry/node' ;
const endpointVolume = new Map <string , { count : number ; resetAt : number }>();
const WINDOW_MS = 60_000 ;
function getAdaptiveRate (name : string , baseRate : number ): number {
const now = Date .now ();
entry = endpointVolume. (name);
(!entry || now > entry. ) {
entry = { : , : now + };
endpointVolume. (name, entry);
}
entry. ++;
(entry. > ) baseRate * ;
(entry. > ) baseRate * ;
baseRate;
}
. ({
: process. . ,
: {
{ name, parentSampled } = samplingContext;
(parentSampled !== ) parentSampled ? : ;
(name?. ( )) ;
(name?. ( )) ;
(name?. ( ) || name?. ( )) ;
(name?. ( )) ( , );
(name?. ( )) (name, );
(name?. ( )) (name, );
(name?. ( )) (name, );
(name?. ( )) (name, );
(name?. ( ) || name?. ( )) {
(name, );
}
(name || , );
},
});
let
get
if
resetAt
count
0
resetAt
WINDOW_MS
set
count
if
count
1000
return
0.25
if
count
100
return
0.5
return
Sentry
init
dsn
env
SENTRY_DSN
tracesSampler
(samplingContext ) =>
const
if
undefined
return
1.0
0
if
match
/\/(health|ready|alive|ping|metrics|favicon)/
return
0
if
match
/\.(css|js|png|jpg|svg|woff2?|ico)$/
return
0
if
includes
'/payment'
includes
'/checkout'
return
1.0
if
includes
'/auth/login'
return
getAdaptiveRate
'auth'
0.5
if
startsWith
'POST /api/'
return
getAdaptiveRate
0.05
if
startsWith
'PUT /api/'
return
getAdaptiveRate
0.05
if
startsWith
'DELETE /api/'
return
getAdaptiveRate
0.05
if
startsWith
'GET /api/'
return
getAdaptiveRate
0.02
if
startsWith
'job:'
startsWith
'queue:'
return
getAdaptiveRate
0.01
return
getAdaptiveRate
'default'
0.005
Adaptive error deduplication with beforeSend:
const errorCounts = new Map <string , number >();
const ERROR_WINDOW_MS = 60_000 ;
setInterval (() => errorCounts.clear (), ERROR_WINDOW_MS );
Sentry .init ({
dsn : process.env .SENTRY_DSN ,
beforeSend (event, hint ) {
const error = hint?.originalException ;
const key = error instanceof Error
? `${error.name} :${error.message?.substring(0 , 100 )} `
: `unknown:${String (event.message || '' ).substring(0 , 100 )} ` ;
const count = (errorCounts.get (key) || 0 ) + 1 ;
errorCounts.set (key, count);
if (count === 1 ) return event;
if (count <= 10 ) return count % 5 === 0 ? event : null ;
if (count <= 100 ) return count % 25 === 0 ? event : null ;
return count % 100 === 0 ? event : null ;
},
});
Step 2 — Optimize SDK for Minimal Overhead At high throughput, every byte and every millisecond of SDK processing matters. This configuration reduces memory footprint, payload size, and CPU time.
import * as Sentry from '@sentry/node' ;
import os from 'node:os' ;
Sentry .init ({
dsn : process.env .SENTRY_DSN ,
environment : process.env .NODE_ENV || 'production' ,
release : `${process.env.SERVICE_NAME} @${process.env.VERSION || 'unknown' } ` ,
maxBreadcrumbs : 15 ,
maxValueLength : 200 ,
integrations : (defaults ) => defaults.filter (i =>
!['Console' , 'ContextLines' ].includes (i.name )
),
profilesSampleRate : 0 ,
transportOptions : {
bufferSize : 100 ,
},
beforeSend (event ) {
if (event.contexts ) {
for (const [key, ctx] of Object .entries (event.contexts )) {
const str = JSON .stringify (ctx);
if (str.length > 2000 ) {
event.contexts [key] = { _truncated : true , originalSize : str.length };
}
}
}
if (event.request ?.headers ) {
const keep = ['content-type' , 'accept' , 'user-agent' , 'x-request-id' ];
event.request .headers = Object .fromEntries (
Object .entries (event.request .headers )
.filter (([k] ) => keep.includes (k.toLowerCase ()))
);
}
return event;
},
serverName : process.env .HOSTNAME || process.env .POD_NAME || os.hostname (),
initialScope : {
tags : {
region : process.env .AWS_REGION || process.env .GCP_REGION || 'unknown' ,
cluster : process.env .K8S_CLUSTER || 'default' ,
pod : process.env .POD_NAME || 'unknown' ,
service : process.env .SERVICE_NAME || 'unknown' ,
},
},
});
Graceful shutdown ensuring event delivery:
import * as Sentry from '@sentry/node' ;
async function shutdown (signal : string ) {
console .log (`${signal} received — flushing Sentry events` );
server.close ();
const flushed = await Sentry .close (2000 );
if (!flushed) {
console .warn ('Sentry flush timed out — some events may be lost' );
}
process.exit (0 );
}
process.on ('SIGTERM' , () => shutdown ('SIGTERM' ));
process.on ('SIGINT' , () => shutdown ('SIGINT' ));
Step 3 — Manage Quotas, Test Under Load, and Plan for Scale Quota management and reserved volume pricing:
Application: 10M requests/day, 0.1% error rate, @sentry/node v8
Error events (with adaptive beforeSend):
Raw errors: 10M x 0.001 = 10,000/day
After dedup: ~1,000/day (90% reduction) = 30K/month
Transaction events (with tiered tracesSampler):
Health/static: 0% of 4M = 0
Payment (T1): 100% of 5K = 5,000/day
POST API (T2): 5% of 500K = 25,000/day
GET API (T3): 2% of 5M = 100,000/day
Other (T5): 0.5% of 500K = 2,500/day
Total: ~132K/day = 4M/month
Sentry Business plan ($26/mo base):
Errors: 30K included in base plan
Transactions: 100K included, overage 3.9M x $0.000025 = ~$97/mo
Estimated total: ~$123/month for 10M requests/day
Reserved volume (if predictable traffic):
5M txns/mo reserved = $80/mo (vs $97 on-demand)
Saves ~$17/mo, locks in price for 12 months
→ Total: ~$106/month
const initStart = performance.now ();
Sentry .init ({ });
const initMs = performance.now () - initStart;
console .log (`Sentry.init: ${initMs.toFixed(1 )} ms` );
import { performance, PerformanceObserver } from 'node:perf_hooks' ;
async function benchmarkOverhead (iterations : number = 1000 ) {
const baseStart = performance.now ();
for (let i = 0 ; i < iterations; i++) {
await handleRequest ({ path : '/api/test' , method : 'GET' });
}
const baseMs = (performance.now () - baseStart) / iterations;
const sentryStart = performance.now ();
for (let i = 0 ; i < iterations; i++) {
await Sentry .startSpan (
{ name : 'GET /api/test' , op : 'http.server' },
() => handleRequest ({ path : '/api/test' , method : 'GET' })
);
}
const sentryMs = (performance.now () - sentryStart) / iterations;
console .log (`Baseline: ${baseMs.toFixed(3 )} ms/req` );
console .log (`With Sentry: ${sentryMs.toFixed(3 )} ms/req` );
console .log (`Overhead: ${(sentryMs - baseMs).toFixed(3 )} ms (${(((sentryMs - baseMs) / baseMs) * 100 ).toFixed(1 )} %)` );
}
Load testing Sentry integration with k6:
import http from 'k6/http' ;
import { check, sleep } from 'k6' ;
import { Rate , Trend } from 'k6/metrics' ;
const errorRate = new Rate ('sentry_errors_captured' );
const latencyOverhead = new Trend ('sentry_latency_overhead_ms' );
export const options = {
stages : [
{ duration : '1m' , target : 50 },
{ duration : '3m' , target : 200 },
{ duration : '1m' , target : 0 },
],
thresholds : {
http_req_duration : ['p(95)<500' ],
sentry_latency_overhead_ms : ['p(95)<5' ],
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000' ;
export default function ( ) {
const readRes = http.get (`${BASE_URL} /api/products` );
check (readRes, { 'GET 200' : (r ) => r.status === 200 });
const sentryMs = readRes.headers ['Server-Timing' ]?.match (/sentry;dur=(\d+\.?\d*)/ );
if (sentryMs) latencyOverhead.add (parseFloat (sentryMs[1 ]));
if (Math .random () < 0.1 ) {
const writeRes = http.post (`${BASE_URL} /api/orders` , JSON .stringify ({
items : [{ sku : 'TEST-001' , qty : 1 }],
}), { headers : { 'Content-Type' : 'application/json' } });
check (writeRes, { 'POST 201' : (r ) => r.status === 201 });
}
if (Math .random () < 0.01 ) {
const errRes = http.get (`${BASE_URL} /api/nonexistent-route` );
errorRate.add (errRes.status === 404 );
}
sleep (0.1 );
}
Background worker batch patterns:
import * as Sentry from '@sentry/node' ;
async function processJobBatch (jobs : Job [] ) {
return Sentry .startSpan (
{
name : `batch.${jobs[0 ]?.type || 'unknown' } ` ,
op : 'queue.batch' ,
attributes : { 'batch.size' : jobs.length },
},
async () => {
const results = { success : 0 , failed : 0 };
for (const job of jobs) {
try {
await Sentry .withScope (async (scope) => {
scope.setTag ('job.type' , job.type );
scope.setTag ('job.queue' , job.queue );
scope.setContext ('job' , {
id : job.id ,
attempts : job.attempts ,
});
await executeJob (job);
results.success ++;
});
} catch (error) {
results.failed ++;
Sentry .captureException (error, {
tags : { 'job.id' : job.id , 'job.type' : job.type },
level : job.attempts >= 3 ? 'error' : 'warning' ,
});
}
}
Sentry .setMeasurement ('batch.success_rate' ,
results.success / jobs.length , 'ratio' );
return results;
}
);
}
setInterval (async () => {
await Sentry .flush (2000 );
}, 30_000 );
Self-hosted Sentry for enterprise (>100M events/month):
Relay: RELAY_PROCESSING_MAX_RATE: 50000, RELAY_UPSTREAM_MAX_CONNECTIONS: 200
Kafka: KAFKA_NUM_PARTITIONS: 32 (match to consumer count)
Snuba: 4+ consumer replicas for Clickhouse ingestion parallelism
Clickhouse: 16G+ RAM, dedicated SSD volumes
Self-hosted vs SaaS break-even:
SaaS at 100M events/month: ~$2,500/mo (Business plan + overage)
Self-hosted (3x r6g.2xlarge): ~$1,200/mo infra + $800/mo ops (0.25 FTE)
Break-even: ~50M events/month
→ Use SaaS up to 50M events; evaluate self-hosted above that
Output
Adaptive sampling reducing duplicate error volume by 90%+ while preserving first-occurrence fidelity
Traffic-aware tracesSampler with 5 tiers adjusting dynamically based on endpoint volume
SDK memory and CPU footprint minimized (15 breadcrumbs, truncated contexts, filtered headers)
Connection pooling via persistent HTTPS agent for efficient event submission
Multi-region infrastructure tags for filtering by region/cluster/pod in Sentry dashboard
Cost model with reserved volume pricing showing $106/month for 10M requests/day
k6 load test script validating Sentry overhead stays under 5ms at p95
Batch job processing pattern with scope isolation and periodic flush
Self-hosted vs SaaS break-even analysis for enterprise decision-making
Error Handling Error Cause Solution Events silently dropped SDK buffer full during traffic spike Increase transportOptions.bufferSize to 200+, verify network to Sentry ingest 429 rate limit from Sentry Quota exhausted or spike protection triggered Enable spike protection in Settings > Subscription, reduce sample rates Memory growing linearly over time Breadcrumb or scope accumulation Reduce maxBreadcrumbs, verify withScope is used (not configureScope) Lost events on deploy/restart No Sentry.close() in shutdown handler Add SIGTERM/SIGINT handlers calling Sentry.close(2000) Distributed traces broken at scale Mixed sampling decisions across services Always check parentSampled first in tracesSampler Clickhouse OOM on self-hosted Insufficient memory for event volume Allocate 16G+ RAM, increase Snuba consumer replicas k6 shows >5ms Sentry overhead Too many integrations or large payloads Disable Console/ContextLines integrations, reduce maxValueLength Quota burn from replay/attachments Replays not rate-limited separately Set replaysSessionSampleRate: 0.01 and replaysOnErrorSampleRate: 0.1
Examples Minimal high-scale init (copy-paste ready):
import * as Sentry from '@sentry/node' ;
Sentry .init ({
dsn : process.env .SENTRY_DSN ,
environment : process.env .NODE_ENV ,
release : `${process.env.SERVICE_NAME} @${process.env.VERSION} ` ,
maxBreadcrumbs : 15 ,
maxValueLength : 200 ,
profilesSampleRate : 0 ,
tracesSampler : ({ name, parentSampled } ) => {
if (parentSampled !== undefined ) return parentSampled ? 1.0 : 0 ;
if (name?.match (/\/(health|ping|metrics)/ )) return 0 ;
if (name?.includes ('/payment' )) return 1.0 ;
if (name?.startsWith ('POST /api/' )) return 0.05 ;
return 0.005 ;
},
});
Verify sampling is working as expected:
Sentry .init ({
tracesSampler : (ctx ) => {
const rate = calculateRate (ctx);
if (process.env .DEBUG_SENTRY === 'true' ) {
console .log (`[sentry] ${ctx.name} → rate=${rate} ` );
}
return rate;
},
});
Resources
Next Steps
Run the k6 load test against staging to establish your baseline Sentry overhead
Set up Sentry Spike Protection (Settings > Subscription > Spike Protection) before going to production
Configure server-side sampling rules in Sentry Dynamic Sampling (Project Settings > Performance) to complement client-side tracesSampler
Create a Sentry dashboard with widgets for: events/hour by category, quota usage %, p95 SDK overhead
Review the sentry-cost-tuning skill for detailed quota optimization strategies