Skip to content

Commit

Permalink
Release v5.18.1
Browse files Browse the repository at this point in the history
Co-authored-by: o.drapeza <[email protected]>
GitOrigin-RevId: 5d290fa88360ce7eeca6faf91f461d840239a3b1
  • Loading branch information
tramvaijsorg and o.drapeza committed Jan 19, 2025
1 parent 86e4f02 commit 0bad2d8
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 13 deletions.
38 changes: 32 additions & 6 deletions docs/guides/server-optimization.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ This guide will contain a set of recommended optimizations to make your applicat

### K8s limits

**TL;DR**: provide **1000m - 1150m** CPU limit/request
**TL;DR**: provide **1000m - 1150m** CPU limit/request, run Node.js with `--max-old-space-size=(0.75% * memory.limit)` parameter

Low CPU limits can significantly slow down your application response time because of CPU throttling. More information why:
- https://medium.com/pipedrive-engineering/how-we-choked-our-kubernetes-nodejs-services-932acc8cc2be
Expand All @@ -42,6 +42,24 @@ Node.js is single-threaded, but can use multiple threads for Garbage Collector o

There is a one disadvantage - with this CPU limits, if you maintain a sufficient number of instances with good latency and throughput, you may experience poor CPU utilization. Nevertheless, this represents a trade-off between effective CPU usage and low latency.

With memory limits, to optimize memory optimization and prevent OOM failures, you need to pass [--max-old-space-size](https://nodejs.org/api/cli.html#--max-old-space-sizesize-in-mib) parameter to Node.js options.

As a start, you can calculate size by the following formula: `0.75 * memory.limit`, or 75% of current container memory limit. Then you can run benchmark and monitor application metrics for a long time, and try to find optimal value. This is recommendation from [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/docker/memory-limit.md) guide.

For setting this parameter you need to run `server.js` using the `node` command with the `--max-old-space-size` parameter. This command is typically located in the `Dockerfile`:

```Dockerfile title="Dockerfile"
FROM node:20-buster-slim
WORKDIR /app
COPY dist/server /app/
COPY package.json /app/
ENV NODE_ENV='production'

EXPOSE 3000
// highlight-next-line
CMD [ "node", "--max-old-space-size=350", "/app/server.js" ]
```

### Request Limiter

**TL;DR**: connect `@tramvai/module-request-limiter` to your application
Expand All @@ -58,30 +76,38 @@ More information:

### Semi space size

**TL;DR**: set Node.js `--max_semi_space_size` parameter to **64mb**
**TL;DR**: set Node.js `--max-semi-space-size` parameter to **32mb** or **64mb**, run benchmarks, and choose the best balance for CPU usage and memory consumption.

[--max-semi-space-size documentation](https://nodejs.org/api/cli.html#--max-semi-space-sizesize-in-mib)

The default value depends on the memory limit. For example, on 64-bit systems
with a memory limit of 512 MiB, the max size of a semi-space defaults to 1 MiB.
On 64-bit systems with a memory limit of 2 GiB, the max size of a semi-space
defaults to 16 MiB.

During application performance profiling, you may observe that your code spends a significant amount of time on Garbage Collector (GC) work. By default, GC work too frequently, and we can reduce the number of GC runs by increasing the size of the semi space. This optimization will reduce the CPU workload and make your event loop less busy, resulting in faster response times, especially in the 95th and 99th percentiles.

One disadvantage of this optimization is that it will increases the memory usage of your application. **For environments, where memory is limited, for example test deployments, prefer not to use this optimization**.

A good balance between performance and memory usage is achieved with a semi space size of **64mb**. Another possible value for this parameter is **128mb**, but it may not provide a significant improvement in performance and will increase the memory usage of your application. It is recommended to test this parameter in your specific application.
A good balance between performance and memory usage is achieved with a semi space size of **64mb**. It is possible to use event bigger value for this parameter, but it may not provide a significant improvement in performance and will highly increase the memory usage of your application. It is recommended to test this parameter in your specific application.

For setting this parameter you need to run `server.js` using the `node` command with the `--max_semi_space_size` parameter. This command is typically located in the `Dockerfile`:
For setting this parameter you need to run `server.js` using the `node` command with the `--max-semi-space-size` parameter. This command is typically located in the `Dockerfile`:

```Dockerfile title="Dockerfile"
FROM node:18-buster-slim
FROM node:20-buster-slim
WORKDIR /app
COPY dist/server /app/
COPY package.json /app/
ENV NODE_ENV='production'

EXPOSE 3000
// highlight-next-line
CMD [ "node", "--max_semi_space_size=64", "/app/server.js" ]
CMD [ "node", "--max-semi-space-size=64", "/app/server.js" ]
```

More information:
- https://www.alibabacloud.com/blog/better-node-application-performance-through-gc-optimization_595119
- https://blog.ztec.fr/en/2024/post/node.js-20-upgrade-journey-though-unexpected-heap-issues-with-kubernetes/
- https://github.com/nodejs/node/issues/42511

### Agent keepAlive
Expand Down
2 changes: 1 addition & 1 deletion packages-versions.json
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

{"@tramvai/cli":"5.17.0","@tramvai/swc-integration":"5.17.0","@tinkoff/browser-timings":"0.13.2","@tinkoff/browserslist-config":"0.5.3","@tinkoff/browser-cookies":"5.0.2","@tinkoff/dippy":"0.11.3","@tinkoff/env-validators":"0.4.2","@tinkoff/error-handlers":"0.8.2","@tinkoff/errors":"0.6.2","@tinkoff/eslint-plugin-tramvai":"0.9.2","@tinkoff/hook-runner":"0.7.3","@tramvai/http-client":"0.5.2","@tinkoff/is-modern-lib":"5.0.2","@tramvai/react-lazy-hydration-render":"0.5.2","@tinkoff/logger":"0.10.505","@tinkoff/pack-polyfills":"0.7.3","@tinkoff/measure-express-requests":"5.0.2","@tinkoff/measure-fastify-requests":"0.4.2","@tinkoff/meta-tags-generate":"0.8.3","@tinkoff/metrics-noop":"5.0.2","@tinkoff/minicss-class-generator":"0.5.2","@tinkoff/mocker":"5.0.3","@tinkoff/module-loader-client":"0.7.3","@tinkoff/module-loader-server":"0.8.4","@tinkoff/monkeypatch":"5.0.2","@tinkoff/package-manager-wrapper":"0.4.2","@tinkoff/htmlpagebuilder":"0.8.2","prettier-config-tinkoff":"0.5.2","@tinkoff/pubsub":"0.8.2","@tinkoff/react-hooks":"0.4.2","@tinkoff/router":"0.5.55","@tramvai/safe-strings":"0.8.4","@tinkoff/terminus":"0.4.2","@tinkoff/layout-factory":"0.6.2","@tramvai/tinkoff-request-http-client-adapter":"0.12.55","@tinkoff/url":"0.11.2","@tinkoff/user-agent":"0.7.55","@tinkoff/webpack-dedupe-plugin":"4.0.2","@tramvai/module-autoscroll":"5.17.0","@tramvai/module-cache-warmup":"5.17.0","@tramvai/module-child-app":"5.17.0","@tramvai/module-client-hints":"5.17.0","@tramvai/module-common":"5.17.0","@tramvai/module-cookie":"5.17.0","@tramvai/module-deps-graph":"5.17.0","@tramvai/module-dns-cache":"5.17.0","@tramvai/module-environment":"5.17.0","@tramvai/module-error-interceptor":"5.17.0","@tramvai/module-http-client":"5.17.0","@tramvai/module-http-proxy-agent":"5.17.0","@tramvai/module-log":"5.17.0","@tramvai/module-metrics":"5.17.0","@tramvai/module-micro-sentry":"5.17.0","@tramvai/module-mocker":"5.17.0","@tramvai/module-opentelemetry":"5.17.0","@tramvai/module-page-render-mode":"5.17.0","@tramvai/module-progressive-web-app":"5.17.0","@tramvai/module-react-query":"5.17.0","@tramvai/module-render":"5.17.0","@tramvai/module-request-limiter":"5.17.0","@tramvai/module-router":"5.17.0","@tramvai/module-sentry":"5.17.0","@tramvai/module-seo":"5.17.0","@tramvai/module-server":"5.17.0","@tramvai/tokens-child-app":"5.17.0","@tramvai/tokens-common":"5.17.0","@tramvai/tokens-cookie":"5.17.0","@tramvai/tokens-core":"5.17.0","@tramvai/tokens-core-private":"5.17.0","@tramvai/tokens-http-client":"5.17.0","@tramvai/tokens-metrics":"5.17.0","@tramvai/tokens-react-query":"5.17.0","@tramvai/tokens-render":"5.17.0","@tramvai/tokens-router":"5.17.0","@tramvai/tokens-server":"5.17.0","@tramvai/tokens-server-private":"5.17.0","@tramvai/child-app-core":"5.17.0","@tramvai/core":"5.17.0","@tramvai/experiments":"5.17.0","@tramvai/papi":"5.17.0","@tramvai/pwa-recipes":"5.17.0","@tramvai/react":"5.17.0","@tramvai/react-query":"5.17.0","@tramvai/state":"5.17.0","@tramvai/storybook-addon":"5.17.0","@tramvai/types-actions-state-context":"5.17.0","@tramvai/test-child-app":"5.17.0","@tramvai/test-helpers":"5.17.0","@tramvai/test-integration":"5.17.0","@tramvai/test-integration-jest":"5.17.0","@tramvai/test-jsdom":"5.17.0","@tramvai/test-mocks":"5.17.0","@tramvai/test-pw":"5.17.0","@tramvai/test-puppeteer":"5.17.0","@tramvai/test-react":"5.17.0","@tramvai/test-unit":"5.17.0","@tramvai/test-unit-jest":"5.17.0","@tramvai/build":"6.1.1","@tramvai/tools-check-versions":"0.7.5","@tramvai/create":"5.17.0","@tramvai/tools-generate-schema":"0.4.2","@tramvai/tools-migrate":"0.9.5","@tinkoff-monorepo/depscheck":"3.101.7","@tinkoff-monorepo/fix-ts-references":"3.101.7","@tinkoff-monorepo/pkgs-collector":"3.101.7","@tinkoff-monorepo/pkgs-collector-dir":"3.101.7","@tinkoff-monorepo/pkgs-collector-workspaces":"3.101.7"}
{"@tramvai/cli":"5.18.1","@tramvai/swc-integration":"5.18.1","@tinkoff/browser-timings":"0.13.2","@tinkoff/browserslist-config":"0.5.3","@tinkoff/browser-cookies":"5.0.2","@tinkoff/dippy":"0.11.3","@tinkoff/env-validators":"0.4.2","@tinkoff/error-handlers":"0.8.2","@tinkoff/errors":"0.6.2","@tinkoff/eslint-plugin-tramvai":"0.9.2","@tinkoff/hook-runner":"0.7.3","@tramvai/http-client":"0.5.2","@tinkoff/is-modern-lib":"5.0.2","@tramvai/react-lazy-hydration-render":"0.5.2","@tinkoff/logger":"0.10.505","@tinkoff/pack-polyfills":"0.7.3","@tinkoff/measure-express-requests":"5.0.2","@tinkoff/measure-fastify-requests":"0.4.2","@tinkoff/meta-tags-generate":"0.8.3","@tinkoff/metrics-noop":"5.0.2","@tinkoff/minicss-class-generator":"0.5.2","@tinkoff/mocker":"5.0.3","@tinkoff/module-loader-client":"0.7.3","@tinkoff/module-loader-server":"0.8.4","@tinkoff/monkeypatch":"5.0.2","@tinkoff/package-manager-wrapper":"0.4.2","@tinkoff/htmlpagebuilder":"0.8.2","prettier-config-tinkoff":"0.5.2","@tinkoff/pubsub":"0.8.2","@tinkoff/react-hooks":"0.4.2","@tinkoff/router":"0.5.58","@tramvai/safe-strings":"0.8.4","@tinkoff/terminus":"0.4.2","@tinkoff/layout-factory":"0.6.2","@tramvai/tinkoff-request-http-client-adapter":"0.12.58","@tinkoff/url":"0.11.2","@tinkoff/user-agent":"0.7.58","@tinkoff/webpack-dedupe-plugin":"4.0.2","@tramvai/module-autoscroll":"5.18.1","@tramvai/module-cache-warmup":"5.18.1","@tramvai/module-child-app":"5.18.1","@tramvai/module-client-hints":"5.18.1","@tramvai/module-common":"5.18.1","@tramvai/module-cookie":"5.18.1","@tramvai/module-deps-graph":"5.18.1","@tramvai/module-dns-cache":"5.18.1","@tramvai/module-environment":"5.18.1","@tramvai/module-error-interceptor":"5.18.1","@tramvai/module-http-client":"5.18.1","@tramvai/module-http-proxy-agent":"5.18.1","@tramvai/module-log":"5.18.1","@tramvai/module-metrics":"5.18.1","@tramvai/module-micro-sentry":"5.18.1","@tramvai/module-mocker":"5.18.1","@tramvai/module-opentelemetry":"5.18.1","@tramvai/module-page-render-mode":"5.18.1","@tramvai/module-progressive-web-app":"5.18.1","@tramvai/module-react-query":"5.18.1","@tramvai/module-render":"5.18.1","@tramvai/module-request-limiter":"5.18.1","@tramvai/module-router":"5.18.1","@tramvai/module-sentry":"5.18.1","@tramvai/module-seo":"5.18.1","@tramvai/module-server":"5.18.1","@tramvai/tokens-child-app":"5.18.1","@tramvai/tokens-common":"5.18.1","@tramvai/tokens-cookie":"5.18.1","@tramvai/tokens-core":"5.18.1","@tramvai/tokens-core-private":"5.18.1","@tramvai/tokens-http-client":"5.18.1","@tramvai/tokens-metrics":"5.18.1","@tramvai/tokens-react-query":"5.18.1","@tramvai/tokens-render":"5.18.1","@tramvai/tokens-router":"5.18.1","@tramvai/tokens-server":"5.18.1","@tramvai/tokens-server-private":"5.18.1","@tramvai/child-app-core":"5.18.1","@tramvai/core":"5.18.1","@tramvai/experiments":"5.18.1","@tramvai/papi":"5.18.1","@tramvai/pwa-recipes":"5.18.1","@tramvai/react":"5.18.1","@tramvai/react-query":"5.18.1","@tramvai/state":"5.18.1","@tramvai/storybook-addon":"5.18.1","@tramvai/types-actions-state-context":"5.18.1","@tramvai/test-child-app":"5.18.1","@tramvai/test-helpers":"5.18.1","@tramvai/test-integration":"5.18.1","@tramvai/test-integration-jest":"5.18.1","@tramvai/test-jsdom":"5.18.1","@tramvai/test-mocks":"5.18.1","@tramvai/test-pw":"5.18.1","@tramvai/test-puppeteer":"5.18.1","@tramvai/test-react":"5.18.1","@tramvai/test-unit":"5.18.1","@tramvai/test-unit-jest":"5.18.1","@tramvai/build":"6.1.1","@tramvai/tools-check-versions":"0.7.5","@tramvai/create":"5.18.1","@tramvai/tools-generate-schema":"0.4.2","@tramvai/tools-migrate":"0.9.5","@tinkoff-monorepo/depscheck":"3.101.7","@tinkoff-monorepo/fix-ts-references":"3.101.7","@tinkoff-monorepo/pkgs-collector":"3.101.7","@tinkoff-monorepo/pkgs-collector-dir":"3.101.7","@tinkoff-monorepo/pkgs-collector-workspaces":"3.101.7"}
2 changes: 1 addition & 1 deletion packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ More details and examples you can find in typescript documentation:
You can use `NODE_OPTIONS` env variable, e.g.:

```bash
NODE_OPTIONS="--max_semi_space_size=64" tramvai start-prod {appName}
NODE_OPTIONS="--max-semi-space-size=64" tramvai start-prod {appName}
```

### How to get CPU profile of @tramvai/cli work?
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/bin/spawn.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ const args = process.argv.slice(2);
const maxMemory = process.env.MAX_USED_MEMORY || '3000';
const maxSemiSpaceSize = process.env.MAX_SEMI_SPACE_SIZE || '128';
const defaultArgs = [
`--max_old_space_size=${maxMemory}`,
`--max_semi_space_size=${maxSemiSpaceSize}`,
`--max-old-space-size=${maxMemory}`,
`--max-semi-space-size=${maxSemiSpaceSize}`,
];

const paramsIndex = args.findIndex((x) => !x.startsWith('-'));
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/utils/clearExecArgv.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/**
* terser-webpack-plugin use jest-workers with worker threads - https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/src/index.js#L407
* used worker from jest-workers pass all execArgv to NodeJS internal worker https://github.com/facebook/jest/blob/main/packages/jest-worker/src/workers/NodeThreadsWorker.ts#L68
* max_old_space_size is not supported by NodeJS internal workers, and provide this error:
* Error [ERR_WORKER_INVALID_EXEC_ARGV]: Initiated Worker with invalid execArgv flags: --max_old_space_size=3000
* max-old-space-size is not supported by NodeJS internal workers, and provide this error:
* Error [ERR_WORKER_INVALID_EXEC_ARGV]: Initiated Worker with invalid execArgv flags: --max-old-space-size=3000
* So, try to remove this flag inside process manually, before worker threads are initialised.
* @TODO: Remove after https://github.com/facebook/jest/pull/12097
*/
export const clearExecArgv = () => {
const index = process.execArgv.findIndex((a) => a.includes('max_old_space_size'));
const index = process.execArgv.findIndex((a) => a.includes('max-old-space-size'));
process.execArgv.splice(index, 1);
};

0 comments on commit 0bad2d8

Please sign in to comment.