Skip to content

Commit 9b71d97

Browse files
authored
feat: report metrics about rate-limiter (#1875)
* feat: report metrics about rate-limiter * refactor: apply PR feedback
1 parent 0043a9d commit 9b71d97

File tree

5 files changed

+52
-12
lines changed

5 files changed

+52
-12
lines changed

content/src/components.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export async function initComponentsWithEnv(env: Environment): Promise<AppCompon
136136
const failedDeployments = await createFailedDeployments({ metrics, database })
137137

138138
const deployRateLimiter = createDeployRateLimiter(
139-
{ logs },
139+
{ logs, metrics },
140140
{
141141
defaultTtl: env.getConfig(EnvironmentConfig.DEPLOYMENTS_DEFAULT_RATE_LIMIT_TTL) ?? ms('1m'),
142142
defaultMax: env.getConfig(EnvironmentConfig.DEPLOYMENTS_DEFAULT_RATE_LIMIT_MAX) ?? 300,
@@ -184,7 +184,7 @@ export async function initComponentsWithEnv(env: Environment): Promise<AppCompon
184184

185185
const validator = { validate }
186186

187-
const serverValidator = createServerValidator({ failedDeployments, metrics, clock })
187+
const serverValidator = createServerValidator({ failedDeployments, clock })
188188

189189
const deployedEntitiesBloomFilter = createDeployedEntitiesBloomFilter({ database, logs, clock })
190190
const deployments = createDeploymentsComponent({ database, logs })

content/src/metrics.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ export const metricsDeclaration = validateMetricsDeclaration({
7272
dcl_content_rate_limited_deployments_total: {
7373
help: 'Total failed deployments due rate limit',
7474
type: 'counter',
75+
labelNames: ['entity_type', 'reason']
76+
},
77+
78+
dcl_content_rate_limiter_cache_keys: {
79+
help: 'Current number of keys in rate limiter caches',
80+
type: 'gauge',
81+
labelNames: ['entity_type', 'cache_type']
82+
},
83+
84+
dcl_content_rate_limiter_cache_max_size: {
85+
help: 'Configured max size for deployment rate limiter cache',
86+
type: 'gauge',
7587
labelNames: ['entity_type']
7688
},
7789

content/src/ports/deployRateLimiterComponent.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export type DeploymentRateLimitConfig = {
2020
}
2121

2222
export function createDeployRateLimiter(
23-
components: Pick<AppComponents, 'logs'>,
23+
components: Pick<AppComponents, 'logs' | 'metrics'>,
2424
rateLimitConfig: DeploymentRateLimitConfig
2525
): IDeployRateLimiterComponent {
2626
const logs: ILoggerComponent.ILogger = components.logs.getLogger('DeployRateLimiterComponent')
@@ -35,6 +35,11 @@ export function createDeployRateLimiter(
3535
rateLimitConfig
3636
)
3737

38+
// Set static max size gauge for each entity type
39+
for (const [entityType, { maxSize }] of deploymentCacheMap) {
40+
components.metrics.observe('dcl_content_rate_limiter_cache_max_size', { entity_type: entityType }, maxSize)
41+
}
42+
3843
function getCacheFromEntityType(entityType: EntityType): { cache: NodeCache; maxSize: number } {
3944
const cache = deploymentCacheMap.get(entityType)
4045
if (!cache) {
@@ -57,28 +62,52 @@ export function createDeployRateLimiter(
5762
for (const pointer of pointers) {
5863
cacheByEntityType.cache.set(pointer, localTimestamp)
5964
}
65+
components.metrics.observe(
66+
'dcl_content_rate_limiter_cache_keys',
67+
{ entity_type: entityType, cache_type: 'deployment' },
68+
cacheByEntityType.cache.stats.keys
69+
)
6070
},
6171

6272
/** Check if the entity should be rate limit: no deployment has been made for the same pointer in the last ttl
6373
* and no more than max size of deployments were made either */
6474
isRateLimited(entityType: EntityType, pointers: string[]): boolean {
6575
const cacheByEntityType = getCacheFromEntityType(entityType)
66-
return (
67-
pointers.some((p) => !!cacheByEntityType.cache.get(p)) ||
68-
cacheByEntityType.cache.stats.keys > cacheByEntityType.maxSize
69-
)
76+
const ttlHit = pointers.some((p) => !!cacheByEntityType.cache.get(p))
77+
const maxSizeHit = cacheByEntityType.cache.stats.keys > cacheByEntityType.maxSize
78+
79+
if (ttlHit || maxSizeHit) {
80+
components.metrics.increment('dcl_content_rate_limited_deployments_total', {
81+
entity_type: entityType,
82+
reason: ttlHit ? 'ttl' : 'max_size'
83+
})
84+
}
85+
86+
return ttlHit || maxSizeHit
7087
},
7188

7289
newUnchangedDeployment(entityType: EntityType, pointers: string[], localTimestamp: number): void {
7390
const cache = getUnchangedCacheFromEntityType(entityType)
7491
for (const pointer of pointers) {
7592
cache.set(pointer, localTimestamp)
7693
}
94+
components.metrics.observe(
95+
'dcl_content_rate_limiter_cache_keys',
96+
{ entity_type: entityType, cache_type: 'unchanged' },
97+
cache.stats.keys
98+
)
7799
},
78100

79101
isUnchangedDeploymentRateLimited(entityType: EntityType, pointers: string[]): boolean {
80102
const cache = getUnchangedCacheFromEntityType(entityType)
81-
return pointers.some((p) => !!cache.get(p))
103+
const limited = pointers.some((p) => !!cache.get(p))
104+
if (limited) {
105+
components.metrics.increment('dcl_content_rate_limited_deployments_total', {
106+
entity_type: entityType,
107+
reason: 'unchanged_ttl'
108+
})
109+
}
110+
return limited
82111
}
83112
}
84113
}

content/src/service/validations/server.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function isRequestTtlForwards(components: Pick<AppComponents, 'clock'>, entity:
3232
const localChecks = async (
3333
entity: Entity,
3434
serviceCalls: ServiceCalls,
35-
components: Pick<AppComponents, 'metrics' | 'clock'>
35+
components: Pick<AppComponents, 'clock'>
3636
): Promise<string | undefined> => {
3737
/** Validate that there are no newer deployments on the entity's pointers */
3838
if (await serviceCalls.areThereNewerEntities(entity))
@@ -46,7 +46,6 @@ const localChecks = async (
4646

4747
/** Validate the deployment is not rate limited */
4848
if (await serviceCalls.isEntityRateLimited(entity)) {
49-
components.metrics.increment('dcl_content_rate_limited_deployments_total', { entity_type: entity.type })
5049
return `Entity rate limited (entityId=${entity.id} pointers=${entity.pointers.join(',')}).`
5150
}
5251

@@ -78,7 +77,7 @@ export const IGNORING_FIX_ERROR = 'Ignoring fix for failed deployment since ther
7877
* Server side validations for current deploying entity for LOCAL and FIX_ATTEMPT contexts
7978
*/
8079
export const createServerValidator = (
81-
components: Pick<AppComponents, 'failedDeployments' | 'metrics' | 'clock'>
80+
components: Pick<AppComponents, 'failedDeployments' | 'clock'>
8281
): ServerValidator => ({
8382
validate: async (entity, context, serviceCalls) => {
8483
// these contexts doesn't validate anything in this side

content/test/integration/ports/deployer/rate-limiting.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ describe('Rate limiting E2E', () => {
3232
*/
3333
function applyRealRateLimiter(): void {
3434
const realRateLimiter = createDeployRateLimiter(
35-
{ logs: server.components.logs },
35+
{ logs: server.components.logs, metrics: server.components.metrics },
3636
{
3737
defaultTtl: NORMAL_TTL_MS,
3838
defaultMax: 10000,

0 commit comments

Comments
 (0)