Skip to content

Commit

Permalink
feat: add new caching route lambda for async routing invocation
Browse files Browse the repository at this point in the history
  • Loading branch information
jsy1218 committed Oct 28, 2024
1 parent 587aca9 commit aa4acaf
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 6 deletions.
112 changes: 112 additions & 0 deletions bin/stacks/routing-lambda-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,70 @@ export class RoutingLambdaStack extends cdk.NestedStack {

const region = cdk.Stack.of(this).region

const cachingRoutingLambda = new aws_lambda_nodejs.NodejsFunction(this, 'CachingRoutingLambda', {
role: lambdaRole,
runtime: aws_lambda.Runtime.NODEJS_18_X,
entry: path.join(__dirname, '../../lib/handlers/index.ts'),
handler: 'quoteHandler',
// 11/8/23: URA currently calls the Routing API with a timeout of 10 seconds.
// Set this lambda's timeout to be slightly lower to give them time to
// log the response in the event of a failure on our end.
timeout: cdk.Duration.seconds(9),
memorySize: 2560,
deadLetterQueueEnabled: true,
bundling: {
minify: true,
sourceMap: true,
},

awsSdkConnectionReuse: true,

description: 'Caching Routing Lambda',
environment: {
VERSION: '1',
NODE_OPTIONS: '--enable-source-maps',
POOL_CACHE_BUCKET: poolCacheBucket.bucketName,
POOL_CACHE_BUCKET_3: poolCacheBucket3.bucketName,
POOL_CACHE_GZIP_KEY: poolCacheGzipKey,
TOKEN_LIST_CACHE_BUCKET: tokenListCacheBucket.bucketName,
ETH_GAS_STATION_INFO_URL: ethGasStationInfoUrl,
TENDERLY_USER: tenderlyUser,
TENDERLY_PROJECT: tenderlyProject,
TENDERLY_ACCESS_KEY: tenderlyAccessKey,
TENDERLY_NODE_API_KEY: tenderlyNodeApiKey,
// WARNING: Dynamo table name should be the tableinstance.name, e.g. routesDynamoDb.tableName.
// But we tried and had seen lambd version error:
// The following resource(s) failed to create: [RoutingLambda2CurrentVersion49A1BB948389ce4f9c26b15e2ccb07b4c1bab726].
// 2023-09-01 10:22:43 UTC-0700RoutingLambda2CurrentVersion49A1BB948389ce4f9c26b15e2ccb07b4c1bab726CREATE_FAILED
// A version for this Lambda function exists ( 261 ). Modify the function to create a new version.
// Hence we do not want to modify the table name below.
ROUTES_TABLE_NAME: DynamoDBTableProps.RoutesDbTable.Name,
ROUTES_CACHING_REQUEST_FLAG_TABLE_NAME: DynamoDBTableProps.RoutesDbCachingRequestFlagTable.Name,
CACHED_ROUTES_TABLE_NAME: DynamoDBTableProps.CacheRouteDynamoDbTable.Name,
CACHING_REQUEST_FLAG_TABLE_NAME: DynamoDBTableProps.CachingRequestFlagDynamoDbTable.Name,
CACHED_V3_POOLS_TABLE_NAME: DynamoDBTableProps.V3PoolsDynamoDbTable.Name,
V2_PAIRS_CACHE_TABLE_NAME: DynamoDBTableProps.V2PairsDynamoCache.Name,
RPC_PROVIDER_HEALTH_TABLE_NAME: DynamoDBTableProps.RpcProviderHealthStateDbTable.Name,

// tokenPropertiesCachingDynamoDb.tableName is the correct format.
// we will start using the correct ones going forward
TOKEN_PROPERTIES_CACHING_TABLE_NAME: tokenPropertiesCachingDynamoDb.tableName,
UNICORN_SECRET: unicornSecret,
GQL_URL: uniGraphQLEndpoint,
GQL_H_ORGN: uniGraphQLHeaderOrigin,
...jsonRpcProviders,
},
layers: [
aws_lambda.LayerVersion.fromLayerVersionArn(
this,
'InsightsLayer',
`arn:aws:lambda:${region}:580247275435:layer:LambdaInsightsExtension:14`
),
],
tracing: aws_lambda.Tracing.ACTIVE,
logRetention: RetentionDays.TWO_WEEKS,
})

this.routingLambda = new aws_lambda_nodejs.NodejsFunction(this, 'RoutingLambda2', {
role: lambdaRole,
runtime: aws_lambda.Runtime.NODEJS_18_X,
Expand Down Expand Up @@ -153,6 +217,7 @@ export class RoutingLambdaStack extends cdk.NestedStack {
UNICORN_SECRET: unicornSecret,
GQL_URL: uniGraphQLEndpoint,
GQL_H_ORGN: uniGraphQLHeaderOrigin,
CACHING_ROUTING_LAMBDA_FUNCTION_NAME: cachingRoutingLambda.functionName,
...jsonRpcProviders,
},
layers: [
Expand All @@ -166,6 +231,23 @@ export class RoutingLambdaStack extends cdk.NestedStack {
logRetention: RetentionDays.TWO_WEEKS,
})

const cachingLambdaAlarmErrorRate = new aws_cloudwatch.Alarm(this, 'RoutingAPI-LambdaErrorRate', {
metric: new aws_cloudwatch.MathExpression({
expression: 'errors / invocations',
usingMetrics: {
errors: cachingRoutingLambda.metricErrors({
period: Duration.minutes(5),
statistic: 'avg',
}),
invocations: cachingRoutingLambda.metricInvocations({
period: Duration.minutes(5),
statistic: 'avg',
}),
},
}),
threshold: 0.05,
evaluationPeriods: 3,
})
const lambdaAlarmErrorRate = new aws_cloudwatch.Alarm(this, 'RoutingAPI-LambdaErrorRate', {
metric: new aws_cloudwatch.MathExpression({
expression: 'errors / invocations',
Expand All @@ -184,6 +266,14 @@ export class RoutingLambdaStack extends cdk.NestedStack {
evaluationPeriods: 3,
})

const cachingLambdaThrottlesErrorRate = new aws_cloudwatch.Alarm(this, 'RoutingAPI-LambdaThrottles', {
metric: cachingRoutingLambda.metricThrottles({
period: Duration.minutes(5),
statistic: 'sum',
}),
threshold: 10,
evaluationPeriods: 3,
})
const lambdaThrottlesErrorRate = new aws_cloudwatch.Alarm(this, 'RoutingAPI-LambdaThrottles', {
metric: this.routingLambda.metricThrottles({
period: Duration.minutes(5),
Expand All @@ -196,20 +286,42 @@ export class RoutingLambdaStack extends cdk.NestedStack {
if (chatbotSNSArn) {
const chatBotTopic = aws_sns.Topic.fromTopicArn(this, 'ChatbotTopic', chatbotSNSArn)

cachingLambdaAlarmErrorRate.addAlarmAction(new aws_cloudwatch_actions.SnsAction(chatBotTopic))
lambdaAlarmErrorRate.addAlarmAction(new aws_cloudwatch_actions.SnsAction(chatBotTopic))

cachingLambdaThrottlesErrorRate.addAlarmAction(new aws_cloudwatch_actions.SnsAction(chatBotTopic))
lambdaThrottlesErrorRate.addAlarmAction(new aws_cloudwatch_actions.SnsAction(chatBotTopic))
}

const enableProvisionedConcurrency = provisionedConcurrency > 0

const cachingRoutingLambdaAlias = new aws_lambda.Alias(this, 'CachingRoutingLiveAlias', {
aliasName: 'live',
version: cachingRoutingLambda.currentVersion,
provisionedConcurrentExecutions: enableProvisionedConcurrency ? provisionedConcurrency : undefined,
})
this.routingLambdaAlias = new aws_lambda.Alias(this, 'RoutingLiveAlias', {
aliasName: 'live',
version: this.routingLambda.currentVersion,
provisionedConcurrentExecutions: enableProvisionedConcurrency ? provisionedConcurrency : undefined,
})

if (enableProvisionedConcurrency) {
const cachingTarget = new asg.ScalableTarget(this, 'CachingRoutingProvConcASG', {
serviceNamespace: asg.ServiceNamespace.LAMBDA,
maxCapacity: provisionedConcurrency * 10,
minCapacity: provisionedConcurrency,
resourceId: `function:${cachingRoutingLambdaAlias.lambda.functionName}:${cachingRoutingLambdaAlias.aliasName}`,
scalableDimension: 'lambda:function:ProvisionedConcurrency',
})

cachingTarget.node.addDependency(cachingRoutingLambdaAlias)

cachingTarget.scaleToTrackMetric('CachingRoutingProvConcTracking', {
targetValue: 0.7,
predefinedMetric: asg.PredefinedMetric.LAMBDA_PROVISIONED_CONCURRENCY_UTILIZATION,
})

const target = new asg.ScalableTarget(this, 'RoutingProvConcASG', {
serviceNamespace: asg.ServiceNamespace.LAMBDA,
maxCapacity: provisionedConcurrency * 10,
Expand Down
25 changes: 19 additions & 6 deletions lib/handlers/injector-sor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export abstract class InjectorSOR<Router, QueryParams> extends Injector<
CACHED_ROUTES_TABLE_NAME,
AWS_LAMBDA_FUNCTION_NAME,
V2_PAIRS_CACHE_TABLE_NAME,
CACHING_ROUTING_LAMBDA_FUNCTION_NAME,
} = process.env

const dependenciesByChain: {
Expand Down Expand Up @@ -467,14 +468,26 @@ export abstract class InjectorSOR<Router, QueryParams> extends Injector<
tenderlySimulator,
ethEstimateGasSimulator
)
const newCachedRoutesRolloutPercent = NEW_CACHED_ROUTES_ROLLOUT_PERCENT[chainId]

let routeCachingProvider: IRouteCachingProvider | undefined = undefined
if (CACHED_ROUTES_TABLE_NAME && CACHED_ROUTES_TABLE_NAME !== '') {
routeCachingProvider = new DynamoRouteCachingProvider({
routesTableName: ROUTES_TABLE_NAME!,
routesCachingRequestFlagTableName: ROUTES_CACHING_REQUEST_FLAG_TABLE_NAME!,
cachingQuoteLambdaName: AWS_LAMBDA_FUNCTION_NAME!,
})

if (Math.random() * 100 < (newCachedRoutesRolloutPercent ?? 0)) {
if (CACHED_ROUTES_TABLE_NAME && CACHED_ROUTES_TABLE_NAME !== '' && CACHING_ROUTING_LAMBDA_FUNCTION_NAME) {
routeCachingProvider = new DynamoRouteCachingProvider({
routesTableName: ROUTES_TABLE_NAME!,
routesCachingRequestFlagTableName: ROUTES_CACHING_REQUEST_FLAG_TABLE_NAME!,
cachingQuoteLambdaName: CACHING_ROUTING_LAMBDA_FUNCTION_NAME,
})
}
} else {
if (CACHED_ROUTES_TABLE_NAME && CACHED_ROUTES_TABLE_NAME !== '') {
routeCachingProvider = new DynamoRouteCachingProvider({
routesTableName: ROUTES_TABLE_NAME!,
routesCachingRequestFlagTableName: ROUTES_CACHING_REQUEST_FLAG_TABLE_NAME!,
cachingQuoteLambdaName: AWS_LAMBDA_FUNCTION_NAME!,
})
}
}

const v2Supported = [
Expand Down

0 comments on commit aa4acaf

Please sign in to comment.