Skip to content

Commit d3776fc

Browse files
authored
add configurable response compression with gzip deflate support + update package json (#1512)
* add configurable response compresion * set true compression for testnet env * refactor processNFtCollections method * update package * update version of node * update unit test yaml * getNfts parallel execution * compression flag false by default * improve getSingleNFt * getNftsCollection increase performance * undo last commit
1 parent a3c8ddf commit d3776fc

10 files changed

Lines changed: 1614 additions & 1423 deletions

File tree

.github/workflows/lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515

1616
strategy:
1717
matrix:
18-
node-version: [16.x]
18+
node-version: [18.x]
1919
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
2020

2121
steps:

.github/workflows/unit.tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515

1616
strategy:
1717
matrix:
18-
node-version: [16.x]
18+
node-version: [18.x]
1919
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
2020

2121
steps:

config/config.devnet.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,8 @@ inflation:
177177
nftProcess:
178178
parallelism: 1
179179
maxRetries: 3
180+
compression:
181+
enabled: true
182+
level: 6
183+
threshold: 1024
184+
chunkSize: 16384

config/config.mainnet.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,8 @@ inflation:
181181
nftProcess:
182182
parallelism: 1
183183
maxRetries: 3
184+
compression:
185+
enabled: true
186+
level: 6
187+
threshold: 1024
188+
chunkSize: 16384

config/config.testnet.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,8 @@ inflation:
180180
nftProcess:
181181
parallelism: 1
182182
maxRetries: 3
183+
compression:
184+
enabled: true
185+
level: 6
186+
threshold: 1024
187+
chunkSize: 16384

package-lock.json

Lines changed: 1463 additions & 1379 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,10 @@
119119
"apollo-server-core": "^3.13.0",
120120
"apollo-server-express": "3.13.0",
121121
"bignumber.js": "^9.0.2",
122+
"compression": "^1.8.0",
122123
"crypto-js": "^4.1.1",
123-
"dataloader": "^2.1.0",
124-
"dd-trace": "^1.6.0",
124+
"dataloader": "^2.2.2",
125+
"dd-trace": "5.56.0",
125126
"fluent-ffmpeg": "^2.1.2",
126127
"graphql": "^16.8.1",
127128
"graphql-fields-list": "^2.2.4",
@@ -141,7 +142,7 @@
141142
"redis": "^3.1.2",
142143
"reflect-metadata": "^0.1.13",
143144
"request-ip": "^2.1.3",
144-
"rimraf": "^3.0.2",
145+
"rimraf": "^5.0.0",
145146
"rxjs": "^7.1.0",
146147
"sharp": "^0.32.6",
147148
"simple-git": "^3.16.0",
@@ -160,6 +161,7 @@
160161
"@nestjs/schematics": "10.0.2",
161162
"@nestjs/testing": "10.2.4",
162163
"@testing-library/jest-dom": "6.1.4",
164+
"@types/compression": "^1.8.1",
163165
"@types/cron": "^1.7.3",
164166
"@types/crypto-js": "^4.0.2",
165167
"@types/express": "^4.17.13",

src/common/api-config/api.config.service.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,4 +934,20 @@ export class ApiConfigService {
934934
getCacheDuration(): number {
935935
return this.configService.get<number>('caching.cacheDuration') ?? 3;
936936
}
937+
938+
getCompressionEnabled(): boolean {
939+
return this.configService.get<boolean>('compression.enabled') ?? false;
940+
}
941+
942+
getCompressionLevel(): number {
943+
return this.configService.get<number>('compression.level') ?? 6;
944+
}
945+
946+
getCompressionThreshold(): number {
947+
return this.configService.get<number>('compression.threshold') ?? 1024;
948+
}
949+
950+
getCompressionChunkSize(): number {
951+
return this.configService.get<number>('compression.chunkSize') ?? 16384;
952+
}
937953
}

src/endpoints/nfts/nft.service.ts

Lines changed: 91 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -69,44 +69,92 @@ export class NftService {
6969

7070
const nfts = await this.getNftsInternal({ from, size }, filter);
7171

72-
for (const nft of nfts) {
73-
await this.applyAssetsAndTicker(nft);
74-
}
72+
await Promise.all([
73+
this.batchApplyAssetsAndTicker(nfts),
74+
this.conditionallyApplyOwners(nfts, queryOptions),
75+
this.conditionallyApplySupply(nfts, queryOptions),
76+
this.batchProcessNfts(nfts),
77+
]);
7578

76-
if (queryOptions && queryOptions.withOwner) {
77-
const nftsIdentifiers = nfts.filter(x => x.type === NftType.NonFungibleESDT).map(x => x.identifier);
79+
await this.batchApplyUnlockFields(nfts);
7880

79-
const accountsEsdts = await this.getAccountEsdtByIdentifiers(nftsIdentifiers, { from: 0, size: nftsIdentifiers.length });
81+
return nfts;
82+
}
8083

81-
for (const nft of nfts) {
82-
if (nft.type === NftType.NonFungibleESDT) {
83-
const accountEsdt = accountsEsdts.find((accountEsdt: any) => accountEsdt.identifier == nft.identifier);
84-
if (accountEsdt) {
85-
nft.owner = accountEsdt.address;
86-
}
84+
private async batchProcessNfts(nfts: Nft[], fields?: string[]) {
85+
await Promise.all([
86+
this.batchApplyMedia(nfts, fields),
87+
this.batchApplyMetadata(nfts, fields),
88+
]);
89+
}
90+
91+
private async batchApplyAssetsAndTicker(nfts: Nft[], fields?: string[]): Promise<void> {
92+
if (fields && fields.includesNone(['ticker', 'assets'])) {
93+
return;
94+
}
95+
96+
await Promise.all(
97+
nfts.map(async (nft) => {
98+
nft.assets = await this.assetsService.getTokenAssets(nft.identifier) ??
99+
await this.assetsService.getTokenAssets(nft.collection);
100+
101+
if (nft.assets) {
102+
nft.ticker = nft.collection.split('-')[0];
103+
} else {
104+
nft.ticker = nft.collection;
87105
}
88-
}
106+
})
107+
);
108+
}
109+
110+
private async conditionallyApplyOwners(nfts: Nft[], queryOptions?: NftQueryOptions): Promise<void> {
111+
if (!queryOptions?.withOwner) {
112+
return;
89113
}
90114

91-
if (queryOptions && queryOptions.withSupply) {
92-
const supplyNfts = nfts.filter(nft => nft.type.in(NftType.SemiFungibleESDT, NftType.MetaESDT));
93-
await this.batchApplySupply(supplyNfts);
115+
const nftsIdentifiers = nfts.filter(x => x.type === NftType.NonFungibleESDT).map(x => x.identifier);
116+
117+
if (nftsIdentifiers.length === 0) {
118+
return;
94119
}
95120

96-
await this.batchProcessNfts(nfts);
121+
const accountsEsdts = await this.getAccountEsdtByIdentifiers(nftsIdentifiers, {
122+
from: 0,
123+
size: nftsIdentifiers.length,
124+
});
125+
126+
const ownerMap = accountsEsdts.reduce((acc: Record<string, string>, accountEsdt: any) => {
127+
acc[accountEsdt.identifier] = accountEsdt.address;
128+
return acc;
129+
}, {});
97130

98131
for (const nft of nfts) {
99-
await this.applyUnlockFields(nft);
132+
if (nft.type === NftType.NonFungibleESDT && ownerMap[nft.identifier]) {
133+
nft.owner = ownerMap[nft.identifier];
134+
}
135+
}
136+
}
137+
138+
private async conditionallyApplySupply(nfts: Nft[], queryOptions?: NftQueryOptions): Promise<void> {
139+
if (!queryOptions?.withSupply) {
140+
return;
100141
}
101142

102-
return nfts;
143+
const supplyNfts = nfts.filter(nft => nft.type.in(NftType.SemiFungibleESDT, NftType.MetaESDT));
144+
145+
if (supplyNfts.length > 0) {
146+
await this.batchApplySupply(supplyNfts);
147+
}
103148
}
104149

105-
private async batchProcessNfts(nfts: Nft[], fields?: string[]) {
106-
await Promise.all([
107-
this.batchApplyMedia(nfts, fields),
108-
this.batchApplyMetadata(nfts, fields),
109-
]);
150+
private async batchApplyUnlockFields(nfts: Nft[], fields?: string[]): Promise<void> {
151+
if (fields && fields.includesNone(['unlockSchedule', 'unlockEpoch'])) {
152+
return;
153+
}
154+
155+
await Promise.all(
156+
nfts.map(nft => this.applyUnlockFields(nft, fields))
157+
);
110158
}
111159

112160
private async applyNftOwner(nft: Nft): Promise<void> {
@@ -219,23 +267,29 @@ export class NftService {
219267
return undefined;
220268
}
221269

222-
if (nft.type && nft.type.in(
223-
NftType.SemiFungibleESDT, NftType.MetaESDT,
224-
NftSubType.DynamicSemiFungibleESDT, NftSubType.DynamicMetaESDT
225-
)) {
226-
await this.applySupply(nft);
227-
}
228-
229-
await this.applyNftOwner(nft);
230-
231-
await this.applyNftAttributes(nft);
270+
const types = [
271+
NftType.SemiFungibleESDT,
272+
NftType.MetaESDT,
273+
NftSubType.DynamicSemiFungibleESDT,
274+
NftSubType.DynamicMetaESDT,
275+
];
232276

233-
await this.applyAssetsAndTicker(nft);
277+
await Promise.all([
278+
(async () => {
279+
if (nft.type && types.includes(nft.type as NftType | NftSubType)) {
280+
await this.applySupply(nft);
281+
}
282+
})(),
283+
this.applyAssetsAndTicker(nft),
284+
this.processNft(nft),
285+
(async () => {
286+
await this.applyNftOwner(nft);
287+
await this.applyNftAttributes(nft);
288+
})(),
289+
]);
234290

235291
await this.applyUnlockFields(nft);
236292

237-
await this.processNft(nft);
238-
239293
return nft;
240294
}
241295

src/main.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import { PrivateAppModule } from './private.app.module';
88
import { CacheWarmerModule } from './crons/cache.warmer/cache.warmer.module';
99
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
1010
import { INestApplication, Logger, NestInterceptor } from '@nestjs/common';
11-
import * as bodyParser from 'body-parser';
12-
import * as requestIp from 'request-ip';
1311
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
1412
import { RedisClient } from 'redis';
1513
import { TransactionProcessorModule } from './crons/transaction.processor/transaction.processor.module';
@@ -35,6 +33,9 @@ import { JwtOrNativeAuthGuard } from '@multiversx/sdk-nestjs-auth';
3533
import { WebSocketPublisherModule } from './common/websockets/web-socket-publisher-module';
3634
import { IndexerService } from './common/indexer/indexer.service';
3735
import { NotWritableError } from './common/indexer/entities/not.writable.error';
36+
import * as bodyParser from 'body-parser';
37+
import * as requestIp from 'request-ip';
38+
import compression from 'compression';
3839

3940
async function bootstrap() {
4041
const logger = new Logger('Bootstrapper');
@@ -178,6 +179,21 @@ async function bootstrap() {
178179
}
179180

180181
async function configurePublicApp(publicApp: NestExpressApplication, apiConfigService: ApiConfigService) {
182+
if (apiConfigService.getCompressionEnabled()) {
183+
publicApp.use(compression({
184+
filter: (req: any, res: any) => {
185+
if (req.headers['x-no-compression']) {
186+
return false;
187+
}
188+
return compression.filter(req, res);
189+
},
190+
level: apiConfigService.getCompressionLevel(),
191+
threshold: apiConfigService.getCompressionThreshold(),
192+
memLevel: 8,
193+
chunkSize: apiConfigService.getCompressionChunkSize(),
194+
}));
195+
}
196+
181197
publicApp.use(bodyParser.json({ limit: '1mb' }));
182198
publicApp.use(requestIp.mw());
183199
publicApp.enableCors();
@@ -297,6 +313,10 @@ async function configurePublicApp(publicApp: NestExpressApplication, apiConfigSe
297313
logger.log(`Use request caching: ${await settingsService.getUseRequestCachingFlag()}`);
298314
logger.log(`Use request logging: ${await settingsService.getUseRequestLoggingFlag()}`);
299315
logger.log(`Use vm query tracing: ${await settingsService.getUseVmQueryTracingFlag()}`);
316+
logger.log(`Compression enabled: ${apiConfigService.getCompressionEnabled()}`);
317+
if (apiConfigService.getCompressionEnabled()) {
318+
logger.log(`Compression level: ${apiConfigService.getCompressionLevel()} (threshold: ${apiConfigService.getCompressionThreshold()} bytes)`);
319+
}
300320
}
301321

302322
async function configureCacheWarmerApp(cacheWarmerApp: INestApplication<any>, apiConfigService: ApiConfigService): Promise<void> {

0 commit comments

Comments
 (0)