Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace step function for limit orders #352

Merged
merged 34 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
545d664
wip: add ecs to track order status
robert-seitz-uniswap Jan 30, 2024
26fec13
wip: add ecs to track order status
robert-seitz-uniswap Jan 30, 2024
3d90175
update ecs job
robert-seitz-uniswap Jan 30, 2024
d74bc23
switch to commonjs
robert-seitz-uniswap Jan 30, 2024
5248d55
adds healthcheck
robert-seitz-uniswap Feb 5, 2024
8e5a9be
cleanup
robert-seitz-uniswap Feb 6, 2024
b8e0ab8
refactor CheckOrderStatusService
robert-seitz-uniswap Feb 8, 2024
c0795db
add tests, refactor
robert-seitz-uniswap Feb 9, 2024
6e48181
lint
robert-seitz-uniswap Feb 9, 2024
d4a9f45
add tests
robert-seitz-uniswap Feb 9, 2024
f360da8
remove ts-nocheck
robert-seitz-uniswap Feb 9, 2024
571dc7c
add metrics to onChainStatusChecker
robert-seitz-uniswap Feb 12, 2024
8c82622
bump lookback to 10 minutes
robert-seitz-uniswap Feb 12, 2024
1658f70
cleanup, upgrade docker to node 18
robert-seitz-uniswap Feb 12, 2024
2f4cbe9
parallelize update requests
robert-seitz-uniswap Feb 12, 2024
1ebf46c
fix expiration bug
robert-seitz-uniswap Feb 13, 2024
e4561f4
add alarms, remove settimeout
robert-seitz-uniswap Feb 13, 2024
21ca9ea
lint
robert-seitz-uniswap Feb 13, 2024
94ca203
refactor OnChainStatusChecker
robert-seitz-uniswap Feb 13, 2024
f6da7c6
cleanup
robert-seitz-uniswap Feb 13, 2024
f63f2df
lint
robert-seitz-uniswap Feb 13, 2024
199f02b
add runInBand
robert-seitz-uniswap Feb 13, 2024
8feb70e
update tests which impact repository
robert-seitz-uniswap Feb 13, 2024
de7444c
add unit tests
robert-seitz-uniswap Feb 13, 2024
f34be61
extract new class
robert-seitz-uniswap Feb 13, 2024
f5e641b
refactor and test
robert-seitz-uniswap Feb 13, 2024
25eed1f
ignore cdk.out tests
robert-seitz-uniswap Feb 13, 2024
c846a73
add alarms
robert-seitz-uniswap Feb 13, 2024
b029b53
change quoteid to be required
robert-seitz-uniswap Feb 13, 2024
b6edd5c
trigger alarm on unsuccesful start
robert-seitz-uniswap Feb 13, 2024
804376c
move test files
robert-seitz-uniswap Feb 13, 2024
c381bce
slight optimization of async calls
robert-seitz-uniswap Feb 14, 2024
e6ff863
update memory limit to 2048, add error rate alarm, remove 8 minute br…
robert-seitz-uniswap Feb 14, 2024
f7d0b04
ensure errors are reported
robert-seitz-uniswap Feb 14, 2024
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
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
build/
dist/
cdk.out/
4 changes: 1 addition & 3 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
"node": true,
"es6": true
},
"plugins": [
"@typescript-eslint"
],
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
Expand Down
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# syntax=docker/dockerfile:experimental
FROM node:18-alpine AS build
WORKDIR /usr/src/app
RUN apk add git

COPY package.json .
COPY yarn.lock .
RUN yarn install
COPY . .

CMD ["npx", "ts-node", "./lib/compute/status.ts"]
1 change: 1 addition & 0 deletions bin/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
// do not change again. Changing would cause every piece of infrastructure to change
// name, and thus be redeployed. Should be camel case and contain no non-alphanumeric characters.
export const SERVICE_NAME = 'GoudaService'
export const HEALTH_CHECK_PORT = 80
8 changes: 8 additions & 0 deletions bin/stacks/lambda-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { STAGE } from '../../lib/util/stage'
import { SERVICE_NAME } from '../constants'
import { CronStack } from './cron-stack'
import { DynamoStack, IndexCapacityConfig, TableCapacityConfig } from './dynamo-stack'
import { StatusStack } from './status-stack'
import { StepFunctionStack } from './step-function-stack'

export interface LambdaStackProps extends cdk.NestedStackProps {
Expand Down Expand Up @@ -93,6 +94,13 @@ export class LambdaStack extends cdk.NestedStack {
indexCapacityConfig,
})

new StatusStack(this, `${SERVICE_NAME}-StatusStack`, {
environmentVariables: {
...props.envVars,
},
stage: props.stage,
})

const sfnStack = new StepFunctionStack(this, `${SERVICE_NAME}SfnStack`, {
stage: props.stage as STAGE,
envVars: {
Expand Down
152 changes: 152 additions & 0 deletions bin/stacks/status-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import * as cdk from 'aws-cdk-lib'
import { aws_ecs, aws_ecs_patterns, aws_iam, Duration, StackProps } from 'aws-cdk-lib'
import { Alarm, ComparisonOperator, MathExpression, Metric, TreatMissingData } from 'aws-cdk-lib/aws-cloudwatch'
import { DockerImageAsset, Platform } from 'aws-cdk-lib/aws-ecr-assets'
import { Cluster, ContainerImage } from 'aws-cdk-lib/aws-ecs'
import { Construct } from 'constructs'
import { OnChainStatusCheckerMetricNames } from '../../lib/Metrics'
import { HEALTH_CHECK_PORT, SERVICE_NAME } from '../constants'

export interface StatusStackProps extends StackProps {
environmentVariables: { [key: string]: string }
stage: string
chatbotSNSArn?: string
}

export class StatusStack extends cdk.NestedStack {
public readonly logDriver: aws_ecs.AwsLogDriver

constructor(scope: Construct, id: string, props: StatusStackProps) {
super(scope, id, props)

const { environmentVariables } = props

this.logDriver = new aws_ecs.AwsLogDriver({
streamPrefix: `${SERVICE_NAME}-StatusStack`,
})

const cluster = new Cluster(this, `Cluster`)

const loaderStackRole = new aws_iam.Role(this, `StatusStackRole`, {
assumedBy: new aws_iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
managedPolicies: [aws_iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonDynamoDBFullAccess')],
})

Metric.grantPutMetricData(loaderStackRole)

const taskDefinition = new aws_ecs.FargateTaskDefinition(this, `TaskDef`, {
taskRole: loaderStackRole,
memoryLimitMiB: 2048,
cpu: 1024,
runtimePlatform: {
operatingSystemFamily: aws_ecs.OperatingSystemFamily.LINUX,
cpuArchitecture: aws_ecs.CpuArchitecture.X86_64,
},
})

const image = new DockerImageAsset(this, `Image`, {
directory: '.',
platform: Platform.LINUX_AMD64,
})

taskDefinition
.addContainer(`TaskContainer`, {
image: ContainerImage.fromDockerImageAsset(image),
// We set EMF environment to local, which causes the metrics to be written to STDOUT,
// and they will be automatically picked up by the cloudwatch log driver defined above.
environment: { ...environmentVariables, AWS_EMF_ENVIRONMENT: 'Local' },
logging: this.logDriver,
})
.addPortMappings({
containerPort: HEALTH_CHECK_PORT,
protocol: aws_ecs.Protocol.TCP,
})

new aws_ecs_patterns.ApplicationLoadBalancedFargateService(this, `StatusService`, {
cluster,
taskDefinition,
desiredCount: 1,
healthCheckGracePeriod: Duration.seconds(60),
})

new Alarm(this, `${SERVICE_NAME}-SEV3-${OnChainStatusCheckerMetricNames.TotalOrderProcessingErrors}`, {
alarmName: `${SERVICE_NAME}-SEV3-${OnChainStatusCheckerMetricNames.TotalOrderProcessingErrors}`,
metric: new Metric({
namespace: 'Uniswap',
metricName: OnChainStatusCheckerMetricNames.TotalOrderProcessingErrors,
dimensionsMap: { service: SERVICE_NAME },
unit: cdk.aws_cloudwatch.Unit.COUNT,
period: Duration.minutes(3),
}),
threshold: 10,
evaluationPeriods: 3,
})

let statusCheckerErrorRate = new MathExpression({
expression: '100*(errors/attempts)',
period: Duration.minutes(3),
usingMetrics: {
errors: new Metric({
namespace: 'Uniswap',
metricName: OnChainStatusCheckerMetricNames.TotalOrderProcessingErrors,
dimensionsMap: { service: SERVICE_NAME },
unit: cdk.aws_cloudwatch.Unit.COUNT,
statistic: 'sum',
}),
attempts: new Metric({
namespace: 'Uniswap',
metricName: OnChainStatusCheckerMetricNames.TotalProcessedOpenOrders,
dimensionsMap: { service: SERVICE_NAME },
unit: cdk.aws_cloudwatch.Unit.COUNT,
statistic: 'sum',
}),
},
})

new Alarm(this, `${SERVICE_NAME}-SEV3-OnChainStatusChecker-ErrorRate`, {
alarmName: `${SERVICE_NAME}-SEV3-OnChainStatusChecker-ErrorRate`,
metric: statusCheckerErrorRate,
threshold: 2,
evaluationPeriods: 3,
})

new Alarm(this, `${SERVICE_NAME}-SEV2-${OnChainStatusCheckerMetricNames.LoopError}`, {
alarmName: `${SERVICE_NAME}-SEV2-${OnChainStatusCheckerMetricNames.LoopError}`,
metric: new Metric({
namespace: 'Uniswap',
metricName: OnChainStatusCheckerMetricNames.LoopError,
dimensionsMap: { service: SERVICE_NAME },
unit: cdk.aws_cloudwatch.Unit.COUNT,
}),
threshold: 1,
evaluationPeriods: 3,
})

new Alarm(this, `${SERVICE_NAME}-SEV2-${OnChainStatusCheckerMetricNames.LoopCompleted}`, {
alarmName: `${SERVICE_NAME}-SEV2-${OnChainStatusCheckerMetricNames.LoopCompleted}`,
metric: new Metric({
namespace: 'Uniswap',
metricName: OnChainStatusCheckerMetricNames.LoopCompleted,
dimensionsMap: { service: SERVICE_NAME },
unit: cdk.aws_cloudwatch.Unit.COUNT,
period: cdk.Duration.minutes(2),
}),
comparisonOperator: ComparisonOperator.LESS_THAN_THRESHOLD,
threshold: 1,
treatMissingData: TreatMissingData.BREACHING,
evaluationPeriods: 3,
})

new Alarm(this, `${SERVICE_NAME}-SEV2-${OnChainStatusCheckerMetricNames.LoopEnded}`, {
alarmName: `${SERVICE_NAME}-SEV2-${OnChainStatusCheckerMetricNames.LoopEnded}`,
metric: new Metric({
namespace: 'Uniswap',
metricName: OnChainStatusCheckerMetricNames.LoopEnded,
dimensionsMap: { service: SERVICE_NAME },
unit: cdk.aws_cloudwatch.Unit.COUNT,
}),
threshold: 1,
evaluationPeriods: 1,
})
}
}
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module.exports = {
...ts_preset,
...dynamo_preset,
testEnvironment: 'node',
testPathIgnorePatterns: ['bin/', 'dist/'],
testPathIgnorePatterns: ['bin/', 'dist/', 'cdk.out/'],
coverageThreshold: {
global: {
statements: 80,
Expand Down
3 changes: 3 additions & 0 deletions lib/Logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Logger } from '@aws-lambda-powertools/logger'

export const log = new Logger()
14 changes: 14 additions & 0 deletions lib/Metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Metrics } from '@aws-lambda-powertools/metrics'
import { SERVICE_NAME } from '../bin/constants'

export const powertoolsMetric = new Metrics({ namespace: 'Uniswap', serviceName: SERVICE_NAME })

const OnChainStatusCheckerPrefix = 'OnChainStatusChecker-'
export const OnChainStatusCheckerMetricNames = {
TotalProcessedOpenOrders: OnChainStatusCheckerPrefix + 'TotalProcessedOpenOrders',
TotalOrderProcessingErrors: OnChainStatusCheckerPrefix + 'TotalOrderProcessingErrors',
TotalLoopProcessingTime: OnChainStatusCheckerPrefix + 'TotalLoopProcessingTime',
LoopError: OnChainStatusCheckerPrefix + 'LoopError',
LoopCompleted: OnChainStatusCheckerPrefix + 'LoopCompleted',
LoopEnded: OnChainStatusCheckerPrefix + 'LoopEnded',
}
24 changes: 24 additions & 0 deletions lib/compute/healthcheck.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import express from 'express'
import { log } from '../Logging'

export interface IHealthCheckServer {
listen(port: number): Promise<void>
}

export class HealthCheckServer implements IHealthCheckServer {
private app: express.Application

constructor(private listenerPort: number) {
this.app = express()

this.app.get('/', (_req, res) => {
res.sendStatus(200)
})
}

async listen(): Promise<void> {
this.app.listen(this.listenerPort, () => {
log.info(`Health check server listening at http://localhost:${this.listenerPort}`)
})
}
}
Loading
Loading