Skip to content

Commit 900237a

Browse files
committed
fix
1 parent 36cf2f4 commit 900237a

21 files changed

+1120
-627
lines changed

.env.production

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ENVIRONMENT=production
2+
DIALECT_SDK_ENVIRONMENT=production

.github/workflows/main.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ jobs:
6363
image_tag: ${{ steps.get-image-tag.outputs.image_tag }}
6464

6565
cd_production:
66-
if: ${{ contains(github.ref, 'heads/master') }}
66+
# if: ${{ contains(github.ref, 'heads/master') }}
6767
needs:
6868
- docker-image
6969
uses: ./.github/workflows/cd.yaml

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM node:16-alpine
1+
FROM node:20-alpine3.18
22

33
WORKDIR /app
44

package.json

+9-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"start:dev": "dotenv -e .env.dev -e .env.service -- nest start --watch",
1414
"start:dev:debug": "dotenv -e .env.dev -e .env.service -- nest start --debug --watch",
1515
"start:local-dev": "dotenv -e .env.local-dev -e .env.service -- nest start --watch",
16+
"start:production": "dotenv -e .env.production -e .env.service -- nest start --watch",
1617
"start:local-dev:debug": "dotenv -e .env.local-dev -e .env.service -- nest start --debug --watch",
1718
"client:start:dev": "dotenv -e .env.dev -e .env.client -- ts-node test/client.ts",
1819
"client:start:local-dev": "dotenv -e .env.local-dev -e .env.client -- ts-node test/client.ts"
@@ -24,14 +25,16 @@
2425
"@nestjs/common": "^9.0.0",
2526
"@nestjs/config": "^2.2.0",
2627
"@nestjs/core": "^9.0.0",
28+
"@nestjs/event-emitter": "^1.3.1",
2729
"@nestjs/platform-express": "^9.0.0",
2830
"@nestjs/schedule": "^2.1.0",
2931
"@nestjs/terminus": "^9.1.0",
30-
"@nestjs/event-emitter": "^1.3.1",
31-
"@solana/spl-governance": "^0.0.34",
32-
"@solana/spl-token": "0.1.8",
32+
"@solana/spl-governance": "^0.3.28",
33+
"@solana/spl-token": "^0.4.3",
3334
"@solana/spl-token-registry": "^0.2.3775",
34-
"bn.js": "^5.2.1",
35+
"bn.js": "^5.1.3",
36+
"borsh": "^0.3.1",
37+
"bs58": "^4.0.1",
3538
"lodash": "^4.17.21",
3639
"luxon": "^3.0.1",
3740
"nestjs-pino": "^2.6.0",
@@ -47,6 +50,7 @@
4750
"@nestjs/schematics": "^9.0.0",
4851
"@nestjs/testing": "^9.0.0",
4952
"@types/bn.js": "^5.1.0",
53+
"@types/bs58": "^4.0.4",
5054
"@types/cron": "^2.0.0",
5155
"@types/express": "^4.17.13",
5256
"@types/jest": "28.1.4",
@@ -56,6 +60,7 @@
5660
"@types/supertest": "^2.0.11",
5761
"@typescript-eslint/eslint-plugin": "^5.0.0",
5862
"@typescript-eslint/parser": "^5.0.0",
63+
"dotenv-cli": "^7.4.1",
5964
"eslint": "^8.0.1",
6065
"eslint-config-prettier": "^8.3.0",
6166
"eslint-plugin-prettier": "^4.0.0",

scripts/twitter.ts

-13
This file was deleted.

src/app.module.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { DialectSdk } from './dialect-sdk';
1111
import { ConfigModule } from '@nestjs/config';
1212
import { RealmsRestService } from './realms-rest-service';
1313
import { RealmsService } from './realms.service';
14-
import { RealmsRepository } from './realms-repository';
14+
import { RealmsCache } from './realms-cache';
1515
import { ScheduleModule } from '@nestjs/schedule';
1616
import { NewProposalsMonitoringService } from './new-proposals-monitoring.service';
1717
import { ProposalStateChangeMonitoringService } from './proposal-state-monitoring.service';
@@ -45,9 +45,9 @@ import { EventEmitterModule } from '@nestjs/event-emitter';
4545
],
4646
controllers: [HealthController],
4747
providers: [
48+
RealmsCache,
4849
CachingHealth,
4950
RealmsRestService,
50-
RealmsRepository,
5151
RealmsService,
5252
NewProposalsMonitoringService,
5353
ProposalStateChangeMonitoringService,

src/caching.health.ts

+66-47
Original file line numberDiff line numberDiff line change
@@ -4,61 +4,80 @@ import {
44
HealthIndicatorResult,
55
} from '@nestjs/terminus';
66
import { Injectable, Logger } from '@nestjs/common';
7-
import { OnEvent } from '@nestjs/event-emitter';
8-
9-
export interface CachingEvent {
10-
type: CachingEventType;
11-
}
12-
13-
export interface CachingStartedEvent extends CachingEvent {
14-
type: CachingEventType.Started;
15-
timeStarted: number;
16-
}
17-
18-
export interface CachingFinishedEvent extends CachingEvent {
19-
type: CachingEventType.Finished;
20-
}
21-
22-
export enum CachingEventType {
23-
Started = 'caching.started',
24-
Finished = 'caching.finished',
25-
}
7+
import { RealmsCache } from './realms-cache';
268

279
@Injectable()
2810
export class CachingHealth extends HealthIndicator {
29-
private static readonly MAX_CACHING_EXECUTION_TIME_MILLIS = process.env
30-
.MAX_CACHING_EXECUTION_TIME_MILLIS
31-
? parseInt(process.env.MAX_CACHING_EXECUTION_TIME_MILLIS, 10)
32-
: 600000;
11+
private static MAX_STATIC_ACCOUNT_CACHE_AGE_MILLIS =
12+
process.env.MAX_CACHE_AGE_MILLIS ?? 3 * 60 * 60 * 1000;
13+
14+
private static MAX_DYNAMIC_ACCOUNT_CACHE_AGE_MILLIS =
15+
process.env.MAX_CACHE_AGE_MILLIS ?? 30 * 60 * 1000;
16+
3317
private readonly logger = new Logger(CachingHealth.name);
34-
private lastStartedCaching: number;
35-
private cachingInProgress = false;
18+
19+
constructor(private readonly realmsCache: RealmsCache) {
20+
super();
21+
}
3622

3723
public isHealthy(): HealthIndicatorResult {
38-
const isHealthy = this.cachingInProgress
39-
? Date.now() - this.lastStartedCaching <
40-
CachingHealth.MAX_CACHING_EXECUTION_TIME_MILLIS
41-
: true;
42-
if (isHealthy) {
43-
return this.getStatus('caching', isHealthy);
24+
if (this.realmsCache.initializationError) {
25+
this.logger.error(
26+
'Caching health check failed. Service needs to be restarted, because initialization failed',
27+
);
28+
throw new HealthCheckError(
29+
'Caching failed',
30+
this.getStatus('caching', false),
31+
);
4432
}
45-
this.logger.error(
46-
'Caching health check failed. Service needs to be restarted',
47-
);
48-
throw new HealthCheckError(
49-
'Caching failed',
50-
this.getStatus('caching', isHealthy),
51-
);
52-
}
5333

54-
@OnEvent(CachingEventType.Started)
55-
onCachingStarted({ timeStarted }: CachingStartedEvent) {
56-
this.lastStartedCaching = timeStarted;
57-
this.cachingInProgress = true;
58-
}
34+
if (!this.realmsCache.isInitialized) {
35+
return this.getStatus('caching', true);
36+
}
37+
38+
if (
39+
!this.realmsCache.lastDynamicAccountCachingSuccessFinishedAt ||
40+
!this.realmsCache.lastStaticAccountCachingSuccessFinishedAt
41+
) {
42+
this.logger.error(
43+
`Some of the cache ages are not initialized, this should not happen.
44+
Static cache age: ${this.realmsCache.lastStaticAccountCachingSuccessFinishedAt}, dynamic cache age: ${this.realmsCache.lastDynamicAccountCachingSuccessFinishedAt}
45+
Service needs to be restarted`,
46+
);
47+
throw new HealthCheckError(
48+
'Caching failed',
49+
this.getStatus('caching', false),
50+
);
51+
}
52+
53+
const staticCacheAge =
54+
Date.now() -
55+
this.realmsCache.lastStaticAccountCachingSuccessFinishedAt.getTime();
56+
57+
if (staticCacheAge > CachingHealth.MAX_STATIC_ACCOUNT_CACHE_AGE_MILLIS) {
58+
this.logger.error(
59+
`Static cache age is too old: ${staticCacheAge}, service needs to be restarted`,
60+
);
61+
throw new HealthCheckError(
62+
'Caching failed',
63+
this.getStatus('caching', false),
64+
);
65+
}
66+
67+
const dynamicCacheAge =
68+
Date.now() -
69+
this.realmsCache.lastDynamicAccountCachingSuccessFinishedAt.getTime();
70+
71+
if (dynamicCacheAge > CachingHealth.MAX_DYNAMIC_ACCOUNT_CACHE_AGE_MILLIS) {
72+
this.logger.error(
73+
`Dynamic cache age is too old: ${dynamicCacheAge}, service needs to be restarted`,
74+
);
75+
throw new HealthCheckError(
76+
'Caching failed',
77+
this.getStatus('caching', false),
78+
);
79+
}
5980

60-
@OnEvent(CachingEventType.Finished)
61-
onCachingFinished() {
62-
this.cachingInProgress = false;
81+
return this.getStatus('caching', true);
6382
}
6483
}

src/dialect-sdk.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import {
88
} from '@dialectlabs/sdk';
99

1010
export abstract class DialectSdk implements IDialectSdk {
11-
readonly dapps: Dapps;
12-
readonly info: DialectSdkInfo;
13-
readonly threads: Messaging;
14-
readonly wallet: Wallets;
15-
readonly identity: IdentityResolver;
11+
readonly dapps!: Dapps;
12+
readonly info!: DialectSdkInfo;
13+
readonly threads!: Messaging;
14+
readonly wallet!: Wallets;
15+
readonly identity!: IdentityResolver;
1616
}

src/health.controller.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ import { CachingHealth } from './caching.health';
1010
export class HealthController {
1111
constructor(
1212
private health: HealthCheckService,
13-
private readonly dataIngestionHealth: CachingHealth,
13+
private readonly cachingHealth: CachingHealth,
1414
) {}
1515

1616
@Get()
1717
@HealthCheck()
1818
check() {
19-
return this.health.check([() => this.dataIngestionHealth.isHealthy()]);
19+
return this.health.check([() => this.cachingHealth.isHealthy()]);
2020
}
2121
}

src/main.ts

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import { NestFactory } from '@nestjs/core';
22
import { AppModule } from './app.module';
33
import { Logger } from 'nestjs-pino';
4-
import { VersioningType } from '@nestjs/common';
4+
import { INestApplication, VersioningType } from '@nestjs/common';
5+
import { Server } from 'http';
56

6-
export const NOTIF_TYPE_ID_PROPOSALS = '04827917-dde4-48c7-bf1b-780b77895e97'
7+
export const NOTIF_TYPE_ID_PROPOSALS = '04827917-dde4-48c7-bf1b-780b77895e97';
78

89
async function bootstrap() {
910
const app = await NestFactory.create(AppModule, {
1011
logger: ['log', 'warn', 'error'],
1112
});
13+
configureHttpServer(app);
14+
configureUnhandledErrorsHandling();
1215
app.setGlobalPrefix('api');
1316
app.enableVersioning({
1417
type: VersioningType.URI,
@@ -24,4 +27,22 @@ async function bootstrap() {
2427
await app.listen(process.env.PORT ?? 0);
2528
}
2629

30+
function configureHttpServer(app: INestApplication) {
31+
// https://shuheikagawa.com/blog/2019/04/25/keep-alive-timeout/
32+
// ALB has default timeout of 60 seconds
33+
const httpAdapter = app.getHttpAdapter();
34+
const server: Server = httpAdapter.getHttpServer();
35+
server.keepAliveTimeout = 61 * 1000;
36+
server.headersTimeout = 65 * 1000;
37+
}
38+
39+
function configureUnhandledErrorsHandling() {
40+
process.on('unhandledRejection', (error) => {
41+
console.error(error);
42+
});
43+
process.on('uncaughtException', (error) => {
44+
console.error(error);
45+
});
46+
}
47+
2748
bootstrap();

0 commit comments

Comments
 (0)