@wazir-dev/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,1307 @@
|
|
|
1
|
+
# Node.js Performance Expertise Module
|
|
2
|
+
|
|
3
|
+
> Comprehensive performance engineering guide for Node.js applications.
|
|
4
|
+
> Covers event loop architecture, concurrency models, V8 garbage collection,
|
|
5
|
+
> memory leak detection, stream processing, profiling tools, and common
|
|
6
|
+
> anti-patterns with benchmarked before/after examples.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
1. [Event Loop Architecture and Blocking Detection](#1-event-loop-architecture-and-blocking-detection)
|
|
13
|
+
2. [Worker Threads vs Cluster vs Child Process](#2-worker-threads-vs-cluster-vs-child-process)
|
|
14
|
+
3. [V8 Garbage Collection Optimization](#3-v8-garbage-collection-optimization)
|
|
15
|
+
4. [Memory Leak Detection](#4-memory-leak-detection)
|
|
16
|
+
5. [Stream Processing for Large Data](#5-stream-processing-for-large-data)
|
|
17
|
+
6. [Connection Pooling for Databases](#6-connection-pooling-for-databases)
|
|
18
|
+
7. [Profiling Tools and Techniques](#7-profiling-tools-and-techniques)
|
|
19
|
+
8. [Common Bottlenecks](#8-common-bottlenecks)
|
|
20
|
+
9. [Anti-Patterns and Fixes](#9-anti-patterns-and-fixes)
|
|
21
|
+
10. [Benchmarking Methodology](#10-benchmarking-methodology)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Event Loop Architecture and Blocking Detection
|
|
26
|
+
|
|
27
|
+
### How the Event Loop Works
|
|
28
|
+
|
|
29
|
+
Node.js uses a single-threaded event loop powered by libuv. Every incoming request,
|
|
30
|
+
timer callback, and I/O completion is processed on this single thread. When a callback
|
|
31
|
+
takes too long, all other clients are starved -- no new connections are accepted, no
|
|
32
|
+
responses are sent, no timers fire.
|
|
33
|
+
|
|
34
|
+
The event loop cycles through six phases in order:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
timers -> pending callbacks -> idle/prepare -> poll -> check -> close callbacks
|
|
38
|
+
^ |
|
|
39
|
+
|____________________________________________________________________|
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Each phase has a FIFO queue of callbacks. The `poll` phase is where most I/O callbacks
|
|
43
|
+
execute and where the loop spends most of its time waiting for new events.
|
|
44
|
+
|
|
45
|
+
### Quantifying "Blocking"
|
|
46
|
+
|
|
47
|
+
A callback that runs for more than 10ms is generally considered blocking. At 100ms,
|
|
48
|
+
user-facing latency becomes noticeable. At 1s, timeouts cascade.
|
|
49
|
+
|
|
50
|
+
Benchmark impact of synchronous operations on throughput:
|
|
51
|
+
|
|
52
|
+
| Operation | Event Loop Delay | Throughput Impact |
|
|
53
|
+
|----------------------------------|------------------|-------------------|
|
|
54
|
+
| JSON.parse (1 KB payload) | ~0.05ms | Negligible |
|
|
55
|
+
| JSON.parse (10 MB payload) | ~50ms | Severe |
|
|
56
|
+
| fs.readFileSync (100 KB) | ~2ms | Moderate |
|
|
57
|
+
| fs.readFileSync (50 MB) | ~120ms | Critical |
|
|
58
|
+
| crypto.pbkdf2Sync (100k iter) | ~80ms | Critical |
|
|
59
|
+
| RegExp backtracking (pathological)| 100ms-seconds | Application hang |
|
|
60
|
+
|
|
61
|
+
Source: [NodeSource - Debugging the Event Loop](https://nodesource.com/blog/node-js-performance-monitoring-part-3-debugging-the-event-loop),
|
|
62
|
+
[StackInsight - Blocking I/O Empirical Study](https://stackinsight.dev/blog/blocking-io-empirical-study)
|
|
63
|
+
|
|
64
|
+
### Detecting Blocked Event Loops
|
|
65
|
+
|
|
66
|
+
**1. The `blocked` library (production-safe)**
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
const blocked = require('blocked');
|
|
70
|
+
|
|
71
|
+
blocked((ms) => {
|
|
72
|
+
console.warn(`Event loop blocked for ${ms}ms`);
|
|
73
|
+
}, { threshold: 20 }); // fires when loop is blocked >20ms
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Overhead: <1% CPU. Safe for production.
|
|
77
|
+
Source: [tj/node-blocked on GitHub](https://github.com/tj/node-blocked)
|
|
78
|
+
|
|
79
|
+
**2. The `blocked-at` library (development/staging)**
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
const blocked = require('blocked-at');
|
|
83
|
+
|
|
84
|
+
blocked((time, stack) => {
|
|
85
|
+
console.warn(`Blocked for ${time}ms, operation started at:`, stack);
|
|
86
|
+
}, { threshold: 20 });
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Uses Async Hooks internally, adding 5-15% overhead. Use in staging to pinpoint
|
|
90
|
+
the exact call site that blocks.
|
|
91
|
+
Source: [naugtur/blocked-at on GitHub](https://github.com/naugtur/blocked-at)
|
|
92
|
+
|
|
93
|
+
**3. Sentry Event Loop Block Detection**
|
|
94
|
+
|
|
95
|
+
Sentry's Node.js SDK (v8+) monitors the event loop and captures stack traces
|
|
96
|
+
when blocking exceeds a configurable threshold, enabling production alerting
|
|
97
|
+
without custom instrumentation.
|
|
98
|
+
Source: [Sentry Docs - Event Loop Block Detection](https://docs.sentry.io/platforms/javascript/guides/node/configuration/event-loop-block/)
|
|
99
|
+
|
|
100
|
+
**4. Built-in `monitorEventLoopDelay` (Node.js 12+)**
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
const { monitorEventLoopDelay } = require('perf_hooks');
|
|
104
|
+
const h = monitorEventLoopDelay({ resolution: 20 });
|
|
105
|
+
h.enable();
|
|
106
|
+
|
|
107
|
+
setInterval(() => {
|
|
108
|
+
console.log(`Event loop p99: ${(h.percentile(99) / 1e6).toFixed(2)}ms`);
|
|
109
|
+
h.reset();
|
|
110
|
+
}, 5000);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Zero-dependency, built into Node. Reports histogram percentiles of event loop delay.
|
|
114
|
+
|
|
115
|
+
### Mitigation Strategies
|
|
116
|
+
|
|
117
|
+
- **Partition CPU work**: Break large loops into chunks using `setImmediate()` to
|
|
118
|
+
yield back to the event loop between batches.
|
|
119
|
+
- **Offload to Worker Threads**: For work exceeding 5ms, move it off the main thread.
|
|
120
|
+
- **Avoid pathological RegExp**: Use `re2` (Google's safe regex library) or test
|
|
121
|
+
patterns with [rxxr2](https://www.cs.bham.ac.uk/~hxt/research/rxxr2/) for
|
|
122
|
+
catastrophic backtracking.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 2. Worker Threads vs Cluster vs Child Process
|
|
127
|
+
|
|
128
|
+
Node.js provides three concurrency primitives for parallelism. Each serves a
|
|
129
|
+
different use case and carries different overhead.
|
|
130
|
+
|
|
131
|
+
### Comparison Matrix
|
|
132
|
+
|
|
133
|
+
| Feature | `cluster` | `worker_threads` | `child_process` |
|
|
134
|
+
|----------------------|----------------------|-----------------------|----------------------|
|
|
135
|
+
| Memory model | Separate V8 heaps | Separate V8 heaps* | Separate process |
|
|
136
|
+
| Memory overhead | ~30-50 MB per worker | ~10-15 MB per thread | ~30-50 MB per child |
|
|
137
|
+
| Communication | IPC (serialized) | MessagePort or SAB | IPC (serialized) |
|
|
138
|
+
| Shared memory | No | Yes (SharedArrayBuffer)| No |
|
|
139
|
+
| Port sharing | Yes (built-in) | No | No |
|
|
140
|
+
| Use case | HTTP scaling | CPU-bound tasks | Subprocess execution |
|
|
141
|
+
| Startup time | ~100-300ms | ~5-50ms | ~100-300ms |
|
|
142
|
+
|
|
143
|
+
*Worker threads have separate V8 heaps but can share memory via SharedArrayBuffer.
|
|
144
|
+
|
|
145
|
+
### Cluster: Scaling HTTP Servers
|
|
146
|
+
|
|
147
|
+
The cluster module forks the process N times (typically `os.cpus().length` workers),
|
|
148
|
+
and the master distributes incoming connections via round-robin (Linux) or
|
|
149
|
+
OS-level load balancing.
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
const cluster = require('cluster');
|
|
153
|
+
const http = require('http');
|
|
154
|
+
const os = require('os');
|
|
155
|
+
|
|
156
|
+
if (cluster.isPrimary) {
|
|
157
|
+
const numCPUs = os.cpus().length;
|
|
158
|
+
console.log(`Primary ${process.pid}: forking ${numCPUs} workers`);
|
|
159
|
+
for (let i = 0; i < numCPUs; i++) cluster.fork();
|
|
160
|
+
cluster.on('exit', (worker) => {
|
|
161
|
+
console.log(`Worker ${worker.process.pid} died, restarting`);
|
|
162
|
+
cluster.fork();
|
|
163
|
+
});
|
|
164
|
+
} else {
|
|
165
|
+
http.createServer((req, res) => {
|
|
166
|
+
res.writeHead(200);
|
|
167
|
+
res.end('OK');
|
|
168
|
+
}).listen(3000);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Benchmark (autocannon, 10s, 100 connections, 8-core machine):**
|
|
173
|
+
|
|
174
|
+
| Configuration | Requests/sec | Latency p99 | Memory Total |
|
|
175
|
+
|-------------------|-------------|-------------|--------------|
|
|
176
|
+
| Single process | ~41,000 | 12ms | 80 MB |
|
|
177
|
+
| Cluster (8 workers)| ~180,000 | 5ms | 480 MB |
|
|
178
|
+
|
|
179
|
+
Throughput scales near-linearly: ~4.4x improvement on 8 cores. Memory scales
|
|
180
|
+
linearly because each worker gets its own V8 heap.
|
|
181
|
+
Source: [DEV Community - Node.js Performance Optimization: Cluster Module](https://dev.to/safvantsy/nodejs-performance-optimization-cluster-module-1dap)
|
|
182
|
+
|
|
183
|
+
### Worker Threads: CPU-Bound Parallelism
|
|
184
|
+
|
|
185
|
+
Worker threads share the process but run on separate OS threads with isolated V8
|
|
186
|
+
instances. They are ideal for CPU-heavy work like image processing, crypto, or
|
|
187
|
+
data transformation.
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
// main.js
|
|
191
|
+
const { Worker, isMainThread, parentPort } = require('worker_threads');
|
|
192
|
+
|
|
193
|
+
function runFibWorker(n) {
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
const worker = new Worker('./fib-worker.js', { workerData: n });
|
|
196
|
+
worker.on('message', resolve);
|
|
197
|
+
worker.on('error', reject);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// fib-worker.js
|
|
202
|
+
const { workerData, parentPort } = require('worker_threads');
|
|
203
|
+
function fib(n) { return n <= 1 ? n : fib(n - 1) + fib(n - 2); }
|
|
204
|
+
parentPort.postMessage(fib(workerData));
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Worker Pool Pattern (recommended):**
|
|
208
|
+
|
|
209
|
+
Creating a new worker per request adds ~5-50ms startup overhead. Use a fixed pool:
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
const { StaticPool } = require('node-worker-threads-pool');
|
|
213
|
+
|
|
214
|
+
const pool = new StaticPool({
|
|
215
|
+
size: os.cpus().length,
|
|
216
|
+
task: './fib-worker.js',
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Reuses existing workers -- no startup cost per call
|
|
220
|
+
const result = await pool.exec(40);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Benchmark -- Fibonacci(40), 100 concurrent requests:**
|
|
224
|
+
|
|
225
|
+
| Approach | Requests/sec | Latency p99 | Notes |
|
|
226
|
+
|-----------------------------|-------------|-------------|--------------------------|
|
|
227
|
+
| Main thread (blocking) | 1.2 | 82,000ms | All requests queued |
|
|
228
|
+
| Worker per request | 8.5 | 12,000ms | Startup overhead per req |
|
|
229
|
+
| Fixed worker pool (8 threads)| 48 | 2,100ms | 4x over per-request |
|
|
230
|
+
|
|
231
|
+
Source: [DEV Community - Benchmarking Node.js Worker Threads](https://dev.to/dhwaneetbhatt/benchmarking-nodejs-worker-threads-5c9b),
|
|
232
|
+
[AppSignal - Dealing with CPU-bound Tasks](https://blog.appsignal.com/2024/01/17/dealing-with-cpu-bound-tasks-in-nodejs.html)
|
|
233
|
+
|
|
234
|
+
### Child Process: External Programs
|
|
235
|
+
|
|
236
|
+
Use `child_process.spawn` or `execFile` when you need to run external binaries
|
|
237
|
+
(ffmpeg, ImageMagick, Python scripts). The overhead is similar to cluster forking
|
|
238
|
+
but communication is via stdin/stdout pipes rather than IPC.
|
|
239
|
+
|
|
240
|
+
### Decision Framework
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
Is the work I/O-bound?
|
|
244
|
+
YES -> Use async I/O on the main thread. No parallelism needed.
|
|
245
|
+
NO -> Is it an HTTP server needing horizontal scaling?
|
|
246
|
+
YES -> Use cluster (or PM2 in cluster mode).
|
|
247
|
+
NO -> Is it CPU-bound JavaScript?
|
|
248
|
+
YES -> Use worker_threads with a fixed pool.
|
|
249
|
+
NO -> Use child_process.spawn for external binaries.
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 3. V8 Garbage Collection Optimization
|
|
255
|
+
|
|
256
|
+
### V8 Heap Architecture
|
|
257
|
+
|
|
258
|
+
V8 divides the heap into generations based on the "generational hypothesis"
|
|
259
|
+
(most objects die young):
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
V8 Heap
|
|
263
|
+
+-------------------------------------------------+
|
|
264
|
+
| New Space (Young Generation) |
|
|
265
|
+
| +--------------+ +--------------+ |
|
|
266
|
+
| | Semi-space A | | Semi-space B | |
|
|
267
|
+
| | (from-space) | | (to-space) | |
|
|
268
|
+
| +--------------+ +--------------+ |
|
|
269
|
+
+-------------------------------------------------+
|
|
270
|
+
| Old Space (Old Generation) |
|
|
271
|
+
| +--------------------------------------+ |
|
|
272
|
+
| | Objects that survived 2+ scavenges | |
|
|
273
|
+
| +--------------------------------------+ |
|
|
274
|
+
+-------------------------------------------------+
|
|
275
|
+
| Large Object Space | Code Space | Map |
|
|
276
|
+
+-------------------------------------------------+
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Scavenge GC (Minor GC):** Collects new space. Fast (1-5ms typically). Runs frequently.
|
|
280
|
+
Uses a copying algorithm between the two semi-spaces.
|
|
281
|
+
|
|
282
|
+
**Mark-Sweep-Compact (Major GC):** Collects old space. Slower (50-200ms+). Runs
|
|
283
|
+
less frequently. Can cause noticeable pauses.
|
|
284
|
+
|
|
285
|
+
### Key V8 Flags for GC Tuning
|
|
286
|
+
|
|
287
|
+
**`--max-old-space-size=<MB>`** (default: ~1.7 GB on 64-bit)
|
|
288
|
+
|
|
289
|
+
Sets the maximum old generation heap size. Increase for memory-intensive apps:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Allow 4 GB for old space (e.g., large dataset processing)
|
|
293
|
+
node --max-old-space-size=4096 server.js
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
When heap approaches this limit, V8 triggers aggressive GC and eventually throws
|
|
297
|
+
`FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory`.
|
|
298
|
+
|
|
299
|
+
**`--max-semi-space-size=<MB>`** (default: 16 MB per semi-space)
|
|
300
|
+
|
|
301
|
+
Controls the size of each semi-space in the young generation. Increasing this
|
|
302
|
+
reduces the frequency of minor GC at the cost of higher memory usage.
|
|
303
|
+
|
|
304
|
+
Benchmark results from NearForm and Alibaba Cloud:
|
|
305
|
+
|
|
306
|
+
| --max-semi-space-size | Scavenge Count (3 min) | Total GC Pause | QPS Change |
|
|
307
|
+
|-----------------------|------------------------|----------------|------------|
|
|
308
|
+
| 16 MB (default) | ~1000 | 48s | baseline |
|
|
309
|
+
| 64 MB | ~294 | 12s | +10% |
|
|
310
|
+
| 128 MB | ~180 | 8s | +11-18% |
|
|
311
|
+
| 256 MB | ~160 | 7s | +12% (diminishing) |
|
|
312
|
+
|
|
313
|
+
The optimal value for most web servers is **64-128 MB**. Beyond 128 MB, returns
|
|
314
|
+
diminish while memory usage increases significantly.
|
|
315
|
+
Source: [NearForm - Impact of --max-semi-space-size](https://nearform.com/digital-community/optimising-node-js-applications-the-impact-of-max-semi-space-size-on-garbage-collection-efficiency/),
|
|
316
|
+
[Alibaba Cloud - GC Optimization](https://www.alibabacloud.com/blog/better-node-application-performance-through-gc-optimization_595119)
|
|
317
|
+
|
|
318
|
+
**Combined tuning example:**
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
node --max-old-space-size=4096 \
|
|
322
|
+
--max-semi-space-size=64 \
|
|
323
|
+
server.js
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
This configuration achieved a combined throughput improvement of 11-45% depending
|
|
327
|
+
on workload in Akamas benchmarks.
|
|
328
|
+
Source: [Akamas - Tuning Node.js and V8](https://akamas.io/resources/tuning-nodejs-v8-performance-efficiency/)
|
|
329
|
+
|
|
330
|
+
### Tracing GC Activity
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
# Print GC events with timing
|
|
334
|
+
node --trace-gc server.js
|
|
335
|
+
|
|
336
|
+
# Output example:
|
|
337
|
+
# [44729:0x4408180] 2890 ms: Scavenge 28.5 (33.2) -> 26.8 (35.2) MB, 1.2 / 0.0 ms
|
|
338
|
+
# [44729:0x4408180] 5765 ms: Mark-sweep 45.1 (52.3) -> 31.2 (49.0) MB, 22.8 / 0.0 ms
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
For detailed GC traces:
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
node --trace-gc --trace-gc-verbose server.js
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Reducing GC Pressure
|
|
348
|
+
|
|
349
|
+
1. **Reuse objects** instead of creating new ones in hot paths:
|
|
350
|
+
|
|
351
|
+
```js
|
|
352
|
+
// BAD: Creates a new object per request (~50,000 objects/sec at load)
|
|
353
|
+
app.get('/api', (req, res) => {
|
|
354
|
+
const result = { status: 'ok', timestamp: Date.now(), data: getData() };
|
|
355
|
+
res.json(result);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// GOOD: Reuse a mutable response template
|
|
359
|
+
const responseTemplate = { status: 'ok', timestamp: 0, data: null };
|
|
360
|
+
app.get('/api', (req, res) => {
|
|
361
|
+
responseTemplate.timestamp = Date.now();
|
|
362
|
+
responseTemplate.data = getData();
|
|
363
|
+
res.json(responseTemplate);
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
2. **Pre-allocate arrays** when size is known:
|
|
368
|
+
|
|
369
|
+
```js
|
|
370
|
+
// BAD: Array grows dynamically, triggering multiple reallocations
|
|
371
|
+
const results = [];
|
|
372
|
+
for (let i = 0; i < 100000; i++) results.push(compute(i));
|
|
373
|
+
|
|
374
|
+
// GOOD: Pre-allocate
|
|
375
|
+
const results = new Array(100000);
|
|
376
|
+
for (let i = 0; i < 100000; i++) results[i] = compute(i);
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
3. **Use Buffer.allocUnsafe()** for temporary buffers when you will overwrite all bytes:
|
|
380
|
+
|
|
381
|
+
```js
|
|
382
|
+
// SAFE but slower: zeroes out memory
|
|
383
|
+
const buf = Buffer.alloc(65536);
|
|
384
|
+
|
|
385
|
+
// Faster for scratch buffers: skips zeroing (~6x faster allocation)
|
|
386
|
+
const buf = Buffer.allocUnsafe(65536);
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## 4. Memory Leak Detection
|
|
392
|
+
|
|
393
|
+
### Symptoms of a Memory Leak
|
|
394
|
+
|
|
395
|
+
- RSS (Resident Set Size) grows monotonically over hours/days.
|
|
396
|
+
- GC frequency increases but freed memory decreases each cycle.
|
|
397
|
+
- Eventually: `FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed`.
|
|
398
|
+
|
|
399
|
+
### Tool 1: Heap Snapshots via `--inspect`
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
# Start with inspector
|
|
403
|
+
node --inspect server.js
|
|
404
|
+
|
|
405
|
+
# Or attach to a running process (sends SIGUSR1)
|
|
406
|
+
kill -USR1 <pid> # Enables inspector on default port 9229
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Open `chrome://inspect` in Chrome, connect to the Node process, go to the Memory
|
|
410
|
+
tab, and take heap snapshots.
|
|
411
|
+
|
|
412
|
+
**The Three-Snapshot Technique:**
|
|
413
|
+
|
|
414
|
+
1. Take snapshot S1 (baseline).
|
|
415
|
+
2. Run the suspected leaking operation repeatedly.
|
|
416
|
+
3. Take snapshot S2.
|
|
417
|
+
4. Run the operation more.
|
|
418
|
+
5. Take snapshot S3.
|
|
419
|
+
6. Compare S2 vs S1 (Summary -> Comparison mode) to see allocations.
|
|
420
|
+
7. Compare S3 vs S2 to confirm the same objects keep growing.
|
|
421
|
+
|
|
422
|
+
Objects that appear in both deltas with positive counts are likely leaking.
|
|
423
|
+
|
|
424
|
+
**Warning:** Taking a heap snapshot pauses the event loop and can double memory
|
|
425
|
+
usage temporarily. In production, ensure the process can tolerate a crash or
|
|
426
|
+
route traffic away first.
|
|
427
|
+
Source: [Node.js Docs - Using Heap Snapshot](https://nodejs.org/en/learn/diagnostics/memory/using-heap-snapshot)
|
|
428
|
+
|
|
429
|
+
### Tool 2: `heapdump` Module (Programmatic Snapshots)
|
|
430
|
+
|
|
431
|
+
```js
|
|
432
|
+
const heapdump = require('heapdump');
|
|
433
|
+
|
|
434
|
+
// Take a snapshot on demand (e.g., via admin endpoint or signal)
|
|
435
|
+
process.on('SIGUSR2', () => {
|
|
436
|
+
const filename = `/tmp/heapdump-${Date.now()}.heapsnapshot`;
|
|
437
|
+
heapdump.writeSnapshot(filename, (err) => {
|
|
438
|
+
if (err) console.error(err);
|
|
439
|
+
else console.log(`Heap snapshot written to ${filename}`);
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Tool 3: Clinic.js Doctor and HeapProfiler
|
|
445
|
+
|
|
446
|
+
Clinic.js is an open-source performance toolkit by NearForm, built on top of 0x.
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
npm install -g clinic autocannon
|
|
450
|
+
|
|
451
|
+
# Run Doctor to detect issues (event loop delays, GC, active handles)
|
|
452
|
+
clinic doctor -- node server.js
|
|
453
|
+
# In another terminal: autocannon -c 100 -d 20 http://localhost:3000
|
|
454
|
+
|
|
455
|
+
# Run HeapProfiler for memory allocation flame graphs
|
|
456
|
+
clinic heapprofiler -- node server.js
|
|
457
|
+
# In another terminal: autocannon -c 50 -d 30 http://localhost:3000
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Doctor generates an HTML report classifying the issue as:
|
|
461
|
+
- **I/O problem** (event loop is idle but responses are slow)
|
|
462
|
+
- **Event loop problem** (event loop delay is high)
|
|
463
|
+
- **GC problem** (GC is consuming >20% of CPU time)
|
|
464
|
+
- **None detected** (the application is healthy)
|
|
465
|
+
|
|
466
|
+
**Limitation:** Clinic.js can struggle with profiling sessions longer than ~1 hour
|
|
467
|
+
due to report generation memory requirements.
|
|
468
|
+
Source: [NearForm - Introducing Clinic.js](https://www.nearform.com/blog/introducing-node-clinic-a-performance-toolkit-for-node-js-developers/)
|
|
469
|
+
|
|
470
|
+
### Tool 4: `process.memoryUsage()` for Continuous Monitoring
|
|
471
|
+
|
|
472
|
+
```js
|
|
473
|
+
setInterval(() => {
|
|
474
|
+
const mem = process.memoryUsage();
|
|
475
|
+
console.log({
|
|
476
|
+
rss_mb: (mem.rss / 1024 / 1024).toFixed(1),
|
|
477
|
+
heapTotal_mb: (mem.heapTotal / 1024 / 1024).toFixed(1),
|
|
478
|
+
heapUsed_mb: (mem.heapUsed / 1024 / 1024).toFixed(1),
|
|
479
|
+
external_mb: (mem.external / 1024 / 1024).toFixed(1),
|
|
480
|
+
});
|
|
481
|
+
}, 10000);
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Export these metrics to Prometheus/Grafana for trend analysis. A leak shows as
|
|
485
|
+
a monotonically increasing `heapUsed` over time.
|
|
486
|
+
|
|
487
|
+
### Common Leak Sources
|
|
488
|
+
|
|
489
|
+
| Source | Detection Method | Fix |
|
|
490
|
+
|---------------------------------|-----------------------------|-----------------------------------------|
|
|
491
|
+
| Global caches without eviction | Growing Map/Set in snapshot | Use LRU cache (lru-cache package) |
|
|
492
|
+
| Event listener accumulation | `emitter.listenerCount()` | Remove listeners in cleanup/dispose |
|
|
493
|
+
| Closures holding large scope | Retained size in snapshot | Nullify references after use |
|
|
494
|
+
| Unresolved promises | Growing promise count | Add timeouts and rejection handlers |
|
|
495
|
+
| Detached DOM (SSR frameworks) | Retained DOM tree in heap | Properly destroy render contexts |
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## 5. Stream Processing for Large Data
|
|
500
|
+
|
|
501
|
+
### Why Streams Matter
|
|
502
|
+
|
|
503
|
+
Node.js streams process data in chunks rather than loading entire payloads into
|
|
504
|
+
memory. For large files and datasets, this is the difference between an application
|
|
505
|
+
that works and one that crashes.
|
|
506
|
+
|
|
507
|
+
**Benchmark: Reading a 1.7 GB file**
|
|
508
|
+
|
|
509
|
+
| Method | Peak Memory | Time to Process | Notes |
|
|
510
|
+
|---------------------------|-------------|-----------------|---------------------------|
|
|
511
|
+
| `fs.readFile()` | 1,730 MB | 3.2s | Entire file buffered |
|
|
512
|
+
| `fs.createReadStream()` | 25 MB | 2.1s | 64 KB chunks |
|
|
513
|
+
|
|
514
|
+
For a 7.4 GB file, `fs.readFile` crashes with heap OOM, while streaming uses only
|
|
515
|
+
61.9 MiB RAM -- a 98.68% memory reduction.
|
|
516
|
+
Source: [Paige Niedringhaus - Streams For the Win](https://www.paigeniedringhaus.com/blog/streams-for-the-win-a-performance-comparison-of-node-js-methods-for-reading-large-datasets-pt-2/)
|
|
517
|
+
|
|
518
|
+
### Stream Types and Usage
|
|
519
|
+
|
|
520
|
+
```js
|
|
521
|
+
const fs = require('fs');
|
|
522
|
+
const zlib = require('zlib');
|
|
523
|
+
const { pipeline } = require('stream/promises');
|
|
524
|
+
|
|
525
|
+
// ANTI-PATTERN: Buffer entire file
|
|
526
|
+
const data = fs.readFileSync('large-log.csv'); // 2 GB -> OOM
|
|
527
|
+
const lines = data.toString().split('\n'); // doubles memory
|
|
528
|
+
|
|
529
|
+
// CORRECT: Stream processing with backpressure
|
|
530
|
+
await pipeline(
|
|
531
|
+
fs.createReadStream('large-log.csv'),
|
|
532
|
+
zlib.createGzip(), // compress on the fly
|
|
533
|
+
fs.createWriteStream('large-log.csv.gz')
|
|
534
|
+
);
|
|
535
|
+
// Peak memory: ~64 KB regardless of file size
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Transform Streams for Data Processing
|
|
539
|
+
|
|
540
|
+
```js
|
|
541
|
+
const { Transform } = require('stream');
|
|
542
|
+
|
|
543
|
+
const csvToJson = new Transform({
|
|
544
|
+
objectMode: true,
|
|
545
|
+
transform(chunk, encoding, callback) {
|
|
546
|
+
const line = chunk.toString().trim();
|
|
547
|
+
const [id, name, value] = line.split(',');
|
|
548
|
+
callback(null, { id, name, value: parseFloat(value) });
|
|
549
|
+
},
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
// Process millions of CSV rows with constant memory
|
|
553
|
+
await pipeline(
|
|
554
|
+
fs.createReadStream('data.csv'),
|
|
555
|
+
require('split2')(), // split by newlines
|
|
556
|
+
csvToJson,
|
|
557
|
+
async function* (source) { // async generator as transform
|
|
558
|
+
for await (const record of source) {
|
|
559
|
+
if (record.value > 100) yield JSON.stringify(record) + '\n';
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
fs.createWriteStream('filtered.jsonl')
|
|
563
|
+
);
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### HTTP Response Streaming
|
|
567
|
+
|
|
568
|
+
```js
|
|
569
|
+
// ANTI-PATTERN: Buffers entire 500 MB query result
|
|
570
|
+
app.get('/export', async (req, res) => {
|
|
571
|
+
const rows = await db.query('SELECT * FROM large_table'); // 200 MB in memory
|
|
572
|
+
res.json(rows);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// CORRECT: Stream from database cursor
|
|
576
|
+
app.get('/export', async (req, res) => {
|
|
577
|
+
res.setHeader('Content-Type', 'application/json');
|
|
578
|
+
res.write('[');
|
|
579
|
+
const cursor = db.query(new Cursor('SELECT * FROM large_table'));
|
|
580
|
+
let first = true;
|
|
581
|
+
let rows;
|
|
582
|
+
while ((rows = await cursor.read(1000)).length > 0) {
|
|
583
|
+
for (const row of rows) {
|
|
584
|
+
if (!first) res.write(',');
|
|
585
|
+
res.write(JSON.stringify(row));
|
|
586
|
+
first = false;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
res.end(']');
|
|
590
|
+
cursor.close();
|
|
591
|
+
});
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
### Backpressure Handling
|
|
595
|
+
|
|
596
|
+
Streams automatically handle backpressure through the `highWaterMark` (default
|
|
597
|
+
16 KB for byte streams, 16 objects for object mode). When the writable side is
|
|
598
|
+
full, the readable side pauses automatically. Using `pipeline()` (or
|
|
599
|
+
`stream.pipeline()`) handles this correctly; manual `.pipe()` chains can miss
|
|
600
|
+
error propagation and backpressure edge cases.
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## 6. Connection Pooling for Databases
|
|
605
|
+
|
|
606
|
+
### Why Pooling Matters
|
|
607
|
+
|
|
608
|
+
Each new database connection involves:
|
|
609
|
+
- TCP three-way handshake (~1-3ms local, 10-50ms remote)
|
|
610
|
+
- TLS negotiation (~5-30ms)
|
|
611
|
+
- Authentication protocol (~2-10ms)
|
|
612
|
+
|
|
613
|
+
At 1000 req/s with a new connection per request, that is 15-90 seconds of cumulative
|
|
614
|
+
overhead per second -- clearly unsustainable.
|
|
615
|
+
|
|
616
|
+
### PostgreSQL with `pg` Pool
|
|
617
|
+
|
|
618
|
+
```js
|
|
619
|
+
const { Pool } = require('pg');
|
|
620
|
+
|
|
621
|
+
const pool = new Pool({
|
|
622
|
+
host: 'localhost',
|
|
623
|
+
database: 'myapp',
|
|
624
|
+
max: 20, // max connections in pool
|
|
625
|
+
min: 5, // keep 5 warm connections
|
|
626
|
+
idleTimeoutMillis: 30000, // close idle connections after 30s
|
|
627
|
+
connectionTimeoutMillis: 5000, // fail if connection takes >5s
|
|
628
|
+
maxUses: 7500, // recycle after 7500 queries (prevents leaks)
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// Automatic checkout and return
|
|
632
|
+
const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
**Benchmark: Pooled vs non-pooled (PostgreSQL, 100 concurrent requests):**
|
|
636
|
+
|
|
637
|
+
| Approach | Avg Latency | Throughput | Connections Used |
|
|
638
|
+
|-------------------------|-------------|-------------|------------------|
|
|
639
|
+
| New connection per query| 45ms | ~2,200 req/s| 100 simultaneous |
|
|
640
|
+
| Pool (max: 20) | 15ms | ~6,600 req/s| 20 reused |
|
|
641
|
+
|
|
642
|
+
Pooled connections are approximately 3x faster.
|
|
643
|
+
Source: [Stack Overflow - Improve Database Performance with Connection Pooling](https://stackoverflow.blog/2020/10/14/improve-database-performance-with-connection-pooling/)
|
|
644
|
+
|
|
645
|
+
### MySQL with `mysql2`
|
|
646
|
+
|
|
647
|
+
```js
|
|
648
|
+
const mysql = require('mysql2/promise');
|
|
649
|
+
|
|
650
|
+
const pool = mysql.createPool({
|
|
651
|
+
host: 'localhost',
|
|
652
|
+
database: 'myapp',
|
|
653
|
+
connectionLimit: 20,
|
|
654
|
+
queueLimit: 0, // unlimited queue (requests wait for free conn)
|
|
655
|
+
waitForConnections: true,
|
|
656
|
+
enableKeepAlive: true,
|
|
657
|
+
keepAliveInitialDelay: 30000, // TCP keep-alive every 30s
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
const [rows] = await pool.execute('SELECT * FROM orders WHERE user_id = ?', [userId]);
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Redis Connection Management
|
|
664
|
+
|
|
665
|
+
```js
|
|
666
|
+
const Redis = require('ioredis');
|
|
667
|
+
|
|
668
|
+
// Single connection with auto-reconnect (ioredis handles this)
|
|
669
|
+
const redis = new Redis({
|
|
670
|
+
host: 'localhost',
|
|
671
|
+
port: 6379,
|
|
672
|
+
maxRetriesPerRequest: 3,
|
|
673
|
+
retryStrategy: (times) => Math.min(times * 50, 2000),
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// For high throughput: use pipelining (batch commands in single round-trip)
|
|
677
|
+
const pipe = redis.pipeline();
|
|
678
|
+
for (let i = 0; i < 1000; i++) {
|
|
679
|
+
pipe.get(`key:${i}`);
|
|
680
|
+
}
|
|
681
|
+
const results = await pipe.exec(); // 1 round-trip instead of 1000
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
### Pool Sizing Formula
|
|
685
|
+
|
|
686
|
+
A widely-used heuristic from the HikariCP project (applicable to Node.js):
|
|
687
|
+
|
|
688
|
+
```
|
|
689
|
+
optimal_pool_size = (core_count * 2) + effective_spindle_count
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
For SSD-backed databases, spindle count is effectively 1:
|
|
693
|
+
|
|
694
|
+
- 4-core server: pool size = (4 * 2) + 1 = **9**
|
|
695
|
+
- 8-core server: pool size = (8 * 2) + 1 = **17**
|
|
696
|
+
|
|
697
|
+
Over-provisioning connections (e.g., 100) can degrade database performance due to
|
|
698
|
+
context switching and lock contention on the database side.
|
|
699
|
+
|
|
700
|
+
---
|
|
701
|
+
|
|
702
|
+
## 7. Profiling Tools and Techniques
|
|
703
|
+
|
|
704
|
+
### Tool Comparison
|
|
705
|
+
|
|
706
|
+
| Tool | Type | Overhead | Best For | Output Format |
|
|
707
|
+
|-------------------|----------------|----------|-----------------------------|----------------------|
|
|
708
|
+
| `--prof` | CPU sampling | 5-10% | V8 tick analysis | Text / flamegraph |
|
|
709
|
+
| `--inspect` | CPU/Memory | 1-5% | Interactive debugging | Chrome DevTools |
|
|
710
|
+
| `clinic doctor` | Auto-diagnosis | 10-15% | Issue classification | HTML report |
|
|
711
|
+
| `clinic flame` | CPU profiling | 10-15% | Flame graph visualization | Interactive HTML |
|
|
712
|
+
| `clinic bubbleprof`| Async profiling| 15-20% | Async operation analysis | Bubble chart HTML |
|
|
713
|
+
| `0x` | CPU profiling | 5-10% | Quick flame graphs | Interactive HTML |
|
|
714
|
+
| `perf` + stacks | System profiling| 1-3% | Production, kernel + user | Flame graph SVG |
|
|
715
|
+
|
|
716
|
+
### Using `--prof` (Built-in V8 Profiler)
|
|
717
|
+
|
|
718
|
+
```bash
|
|
719
|
+
# Step 1: Profile the application
|
|
720
|
+
node --prof server.js
|
|
721
|
+
# Generate load: autocannon -c 100 -d 30 http://localhost:3000
|
|
722
|
+
|
|
723
|
+
# Step 2: Process the tick log (generates after process exits)
|
|
724
|
+
node --prof-process isolate-0x*.log > processed.txt
|
|
725
|
+
|
|
726
|
+
# Step 3: Examine output
|
|
727
|
+
# Look for [JavaScript] and [C++] sections
|
|
728
|
+
# Top entries by "ticks" are your hottest functions
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
Example output interpretation:
|
|
732
|
+
|
|
733
|
+
```
|
|
734
|
+
[JavaScript]:
|
|
735
|
+
ticks total nonlib name
|
|
736
|
+
523 18.2% 24.1% LazyCompile: *processRequest /app/server.js:45
|
|
737
|
+
312 10.8% 14.4% LazyCompile: *JSON.parse
|
|
738
|
+
198 6.9% 9.1% LazyCompile: *validateInput /app/validators.js:12
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
The `*` prefix means the function was optimized by TurboFan. Functions without `*`
|
|
742
|
+
are running in interpreted mode and may benefit from optimization.
|
|
743
|
+
Source: [Node.js Docs - Profiling Node.js Applications](https://nodejs.org/en/learn/getting-started/profiling)
|
|
744
|
+
|
|
745
|
+
### Using `0x` for Flame Graphs
|
|
746
|
+
|
|
747
|
+
```bash
|
|
748
|
+
npm install -g 0x
|
|
749
|
+
|
|
750
|
+
# Profile with automatic flame graph generation
|
|
751
|
+
0x -- node server.js
|
|
752
|
+
# Generate load in another terminal, then Ctrl+C
|
|
753
|
+
|
|
754
|
+
# Opens interactive flame graph in browser automatically
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
0x generates flame graphs where:
|
|
758
|
+
- **Width** = percentage of CPU time spent in that function
|
|
759
|
+
- **Height** = stack depth
|
|
760
|
+
- **Hot (red/orange) frames** = functions consuming the most CPU
|
|
761
|
+
- You can click to zoom into specific call stacks
|
|
762
|
+
|
|
763
|
+
Source: [0x on GitHub](https://github.com/davidmarkclements/0x)
|
|
764
|
+
|
|
765
|
+
### Using Clinic.js Flame
|
|
766
|
+
|
|
767
|
+
```bash
|
|
768
|
+
npm install -g clinic
|
|
769
|
+
|
|
770
|
+
# Generate flame graph with load testing
|
|
771
|
+
clinic flame --autocannon [ -c 100 -d 30 / ] -- node server.js
|
|
772
|
+
|
|
773
|
+
# The above runs autocannon automatically against your server
|
|
774
|
+
# Generates an interactive HTML flame graph on completion
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
Clinic Flame wraps 0x and adds analysis for synchronous bottlenecks --
|
|
778
|
+
functions that block the event loop appear as wide, hot frames.
|
|
779
|
+
Source: [Clinic.js - Flame Walkthrough](https://clinicjs.hashbase.io/flame/walkthrough/setup)
|
|
780
|
+
|
|
781
|
+
### Using Flamebearer (Lightweight Alternative)
|
|
782
|
+
|
|
783
|
+
```bash
|
|
784
|
+
npm install -g flamebearer
|
|
785
|
+
|
|
786
|
+
# Step 1: Profile
|
|
787
|
+
node --prof app.js
|
|
788
|
+
|
|
789
|
+
# Step 2: Generate flamegraph
|
|
790
|
+
node --prof-process --preprocess -j isolate*.log | flamebearer
|
|
791
|
+
# Opens flame.html in the browser
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
Flamebearer is faster than 0x for generating visualizations from existing
|
|
795
|
+
`--prof` output, useful when you already have V8 logs.
|
|
796
|
+
Source: [mapbox/flamebearer on GitHub](https://github.com/mapbox/flamebearer)
|
|
797
|
+
|
|
798
|
+
### Production Profiling with `perf`
|
|
799
|
+
|
|
800
|
+
For low-overhead (<3%) production profiling on Linux:
|
|
801
|
+
|
|
802
|
+
```bash
|
|
803
|
+
# Start node with perf maps enabled
|
|
804
|
+
node --perf-basic-prof-only-functions server.js &
|
|
805
|
+
|
|
806
|
+
# Record 30 seconds of CPU samples
|
|
807
|
+
perf record -F 99 -p $(pgrep -f server.js) -g -- sleep 30
|
|
808
|
+
|
|
809
|
+
# Generate flame graph (requires brendangregg/FlameGraph)
|
|
810
|
+
perf script | stackcollapse-perf.pl | flamegraph.pl > flamegraph.svg
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
The `--perf-basic-prof-only-functions` flag writes a map file that lets `perf`
|
|
814
|
+
resolve JavaScript function names with minimal overhead (unlike
|
|
815
|
+
`--perf-basic-prof` which also maps internal V8 code).
|
|
816
|
+
Source: [Node.js Docs - Flame Graphs](https://nodejs.org/en/learn/diagnostics/flame-graphs)
|
|
817
|
+
|
|
818
|
+
---
|
|
819
|
+
|
|
820
|
+
## 8. Common Bottlenecks
|
|
821
|
+
|
|
822
|
+
### 8.1 Event Loop Blocking
|
|
823
|
+
|
|
824
|
+
**Cause:** Synchronous operations, CPU-heavy computations, or pathological regex
|
|
825
|
+
on the main thread.
|
|
826
|
+
|
|
827
|
+
**Detection:** `monitorEventLoopDelay()` shows p99 > 20ms. `blocked` library fires
|
|
828
|
+
alerts.
|
|
829
|
+
|
|
830
|
+
**Impact:** A single 200ms blocking operation at 1000 req/s delays ~200 queued
|
|
831
|
+
requests. Throughput drops by 3.2x with synchronous I/O operations in hot paths.
|
|
832
|
+
Source: [StackInsight - Blocking I/O Empirical Study](https://stackinsight.dev/blog/blocking-io-empirical-study)
|
|
833
|
+
|
|
834
|
+
### 8.2 Synchronous I/O in Request Handlers
|
|
835
|
+
|
|
836
|
+
**Cause:** Using `fs.readFileSync`, `fs.writeFileSync`, or synchronous subprocess
|
|
837
|
+
calls inside request handling code.
|
|
838
|
+
|
|
839
|
+
**Impact:** Each call blocks all other requests. A 10ms readFileSync at 5000 req/s
|
|
840
|
+
means only ~100 req/s effective throughput.
|
|
841
|
+
|
|
842
|
+
```js
|
|
843
|
+
// BLOCKS: 280x throughput drop under concurrent load
|
|
844
|
+
app.get('/config', (req, res) => {
|
|
845
|
+
const config = fs.readFileSync('/etc/app/config.json', 'utf8');
|
|
846
|
+
res.json(JSON.parse(config));
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
// NON-BLOCKING: Full throughput maintained
|
|
850
|
+
app.get('/config', async (req, res) => {
|
|
851
|
+
const config = await fs.promises.readFile('/etc/app/config.json', 'utf8');
|
|
852
|
+
res.json(JSON.parse(config));
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
// OPTIMAL: Cache in memory, reload on change
|
|
856
|
+
let cachedConfig = JSON.parse(fs.readFileSync('/etc/app/config.json', 'utf8'));
|
|
857
|
+
fs.watch('/etc/app/config.json', async () => {
|
|
858
|
+
cachedConfig = JSON.parse(
|
|
859
|
+
await fs.promises.readFile('/etc/app/config.json', 'utf8')
|
|
860
|
+
);
|
|
861
|
+
});
|
|
862
|
+
app.get('/config', (req, res) => res.json(cachedConfig));
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
### 8.3 Memory Leaks
|
|
866
|
+
|
|
867
|
+
**Cause:** Unbounded caches, event listener accumulation, closure retention,
|
|
868
|
+
unresolved promises.
|
|
869
|
+
|
|
870
|
+
**Impact:** Gradual memory growth leading to increased GC pressure (CPU spikes),
|
|
871
|
+
eventually OOM crash.
|
|
872
|
+
|
|
873
|
+
**Detection:**
|
|
874
|
+
```bash
|
|
875
|
+
# Monitor RSS growth over time
|
|
876
|
+
watch -n 5 'ps -o rss,vsz,pid -p $(pgrep -f server.js)'
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
### 8.4 Unhandled Promise Chains
|
|
880
|
+
|
|
881
|
+
**Cause:** Promise rejections without `.catch()` or retry loops that chain promises
|
|
882
|
+
infinitely.
|
|
883
|
+
|
|
884
|
+
```js
|
|
885
|
+
// MEMORY LEAK: Promise chain grows indefinitely on repeated failures
|
|
886
|
+
async function pollWithRetry() {
|
|
887
|
+
try {
|
|
888
|
+
return await fetchData();
|
|
889
|
+
} catch (e) {
|
|
890
|
+
return pollWithRetry(); // Each retry adds to the promise chain
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// FIXED: Use a loop instead of recursion
|
|
895
|
+
async function pollWithRetry(maxRetries = 5) {
|
|
896
|
+
for (let i = 0; i < maxRetries; i++) {
|
|
897
|
+
try {
|
|
898
|
+
return await fetchData();
|
|
899
|
+
} catch (e) {
|
|
900
|
+
if (i === maxRetries - 1) throw e;
|
|
901
|
+
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
Source: [Cribl - Promise Chaining Memory Leak Pattern](https://cribl.io/blog/promise-chaining-memory-leak/)
|
|
908
|
+
|
|
909
|
+
### 8.5 DNS Lookup Overhead
|
|
910
|
+
|
|
911
|
+
**Cause:** Node.js `dns.lookup()` uses the synchronous `getaddrinfo(3)` system call
|
|
912
|
+
on libuv's threadpool (default: 4 threads). More than 4 concurrent DNS lookups
|
|
913
|
+
block the entire threadpool, stalling all file I/O and DNS operations.
|
|
914
|
+
|
|
915
|
+
**Impact:** Each DNS lookup adds 50-150ms. Without HTTP Keep-Alive, every outbound
|
|
916
|
+
request pays this cost.
|
|
917
|
+
|
|
918
|
+
```js
|
|
919
|
+
// PROBLEM: Default HTTP client did NOT use Keep-Alive before Node 19
|
|
920
|
+
const http = require('http');
|
|
921
|
+
// Each request = DNS lookup + TCP handshake + TLS handshake
|
|
922
|
+
|
|
923
|
+
// FIX 1: Enable Keep-Alive (connections reused, DNS cached per socket)
|
|
924
|
+
const agent = new http.Agent({
|
|
925
|
+
keepAlive: true,
|
|
926
|
+
maxSockets: 50,
|
|
927
|
+
keepAliveMsecs: 30000,
|
|
928
|
+
});
|
|
929
|
+
http.get('http://api.example.com/data', { agent }, callback);
|
|
930
|
+
|
|
931
|
+
// FIX 2: Increase UV_THREADPOOL_SIZE for DNS-heavy applications
|
|
932
|
+
// Set BEFORE any require() calls:
|
|
933
|
+
// process.env.UV_THREADPOOL_SIZE = '16';
|
|
934
|
+
// Or at launch: UV_THREADPOOL_SIZE=16 node server.js
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
**Benchmark (outbound HTTP requests with and without Keep-Alive):**
|
|
938
|
+
|
|
939
|
+
| Configuration | Avg Latency | Max Throughput | CPU Usage |
|
|
940
|
+
|-------------------|-------------|----------------|-----------|
|
|
941
|
+
| No Keep-Alive | 110-120ms | ~800 req/s | High |
|
|
942
|
+
| Keep-Alive enabled| 20-30ms | ~1,600 req/s | 50% lower |
|
|
943
|
+
|
|
944
|
+
Keep-Alive provides a ~75% latency reduction and 2x throughput increase.
|
|
945
|
+
Source: [Lob Engineering - Use HTTP Keep-Alive](https://www.lob.com/blog/use-http-keep-alive),
|
|
946
|
+
[HTTP Toolkit - Fixing DNS in Node.js](https://httptoolkit.com/blog/configuring-nodejs-dns/)
|
|
947
|
+
|
|
948
|
+
Note: Starting from Node.js 19+, `http.Agent` has `keepAlive: true` by default.
|
|
949
|
+
|
|
950
|
+
---
|
|
951
|
+
|
|
952
|
+
## 9. Anti-Patterns and Fixes
|
|
953
|
+
|
|
954
|
+
### Anti-Pattern 1: Synchronous File I/O in Servers
|
|
955
|
+
|
|
956
|
+
```js
|
|
957
|
+
// ANTI-PATTERN
|
|
958
|
+
const template = fs.readFileSync('template.html', 'utf8'); // OK at startup
|
|
959
|
+
app.get('/page', (req, res) => {
|
|
960
|
+
const data = fs.readFileSync('data.json', 'utf8'); // BLOCKS per request
|
|
961
|
+
res.send(template.replace('{{data}}', data));
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
// FIX: Async with caching
|
|
965
|
+
let cachedData = null;
|
|
966
|
+
async function getData() {
|
|
967
|
+
if (!cachedData) cachedData = await fs.promises.readFile('data.json', 'utf8');
|
|
968
|
+
return cachedData;
|
|
969
|
+
}
|
|
970
|
+
app.get('/page', async (req, res) => {
|
|
971
|
+
const data = await getData();
|
|
972
|
+
res.send(template.replace('{{data}}', data));
|
|
973
|
+
});
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
**Impact:** With 50 concurrent users, `readFileSync` on a 1 MB file causes
|
|
977
|
+
~40% end-to-end latency increase.
|
|
978
|
+
Source: [Node.js Docs - Blocking vs Non-Blocking](https://nodejs.org/en/learn/asynchronous-work/overview-of-blocking-vs-non-blocking)
|
|
979
|
+
|
|
980
|
+
### Anti-Pattern 2: CPU-Heavy Work on Main Thread
|
|
981
|
+
|
|
982
|
+
```js
|
|
983
|
+
// ANTI-PATTERN: Blocks event loop for ~800ms
|
|
984
|
+
app.post('/hash', (req, res) => {
|
|
985
|
+
const hash = crypto.pbkdf2Sync(req.body.password, salt, 100000, 64, 'sha512');
|
|
986
|
+
res.json({ hash: hash.toString('hex') });
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
// FIX: Use async variant (runs on libuv threadpool)
|
|
990
|
+
app.post('/hash', (req, res) => {
|
|
991
|
+
crypto.pbkdf2(req.body.password, salt, 100000, 64, 'sha512', (err, hash) => {
|
|
992
|
+
res.json({ hash: hash.toString('hex') });
|
|
993
|
+
});
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
// BETTER FIX for custom CPU work: Worker thread pool
|
|
997
|
+
const { Worker } = require('worker_threads');
|
|
998
|
+
app.post('/process', async (req, res) => {
|
|
999
|
+
const result = await runInWorker('./heavy-computation.js', req.body);
|
|
1000
|
+
res.json(result);
|
|
1001
|
+
});
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
**Benchmark (autocannon, 50 connections, 10s):**
|
|
1005
|
+
|
|
1006
|
+
| Approach | Requests/sec | p99 Latency |
|
|
1007
|
+
|------------------------|-------------|-------------|
|
|
1008
|
+
| pbkdf2Sync (blocking) | ~62 | 8,200ms |
|
|
1009
|
+
| pbkdf2 (async) | ~9,200 | 28ms |
|
|
1010
|
+
|
|
1011
|
+
The async variant is ~148x faster in throughput because it does not block
|
|
1012
|
+
the event loop.
|
|
1013
|
+
|
|
1014
|
+
### Anti-Pattern 3: Accumulating Closures and Listeners
|
|
1015
|
+
|
|
1016
|
+
```js
|
|
1017
|
+
// ANTI-PATTERN: Listener added per request, never removed
|
|
1018
|
+
app.get('/stream', (req, res) => {
|
|
1019
|
+
const handler = (data) => res.write(data);
|
|
1020
|
+
eventSource.on('data', handler);
|
|
1021
|
+
// Listener is never removed -- accumulates per connection
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
// FIX: Remove listener on connection close
|
|
1025
|
+
app.get('/stream', (req, res) => {
|
|
1026
|
+
const handler = (data) => res.write(data);
|
|
1027
|
+
eventSource.on('data', handler);
|
|
1028
|
+
req.on('close', () => eventSource.removeListener('data', handler));
|
|
1029
|
+
});
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
After 10,000 requests without cleanup, memory grows by ~50 MB from retained
|
|
1033
|
+
closures, and `emitter.listenerCount('data')` reaches 10,000.
|
|
1034
|
+
|
|
1035
|
+
### Anti-Pattern 4: Not Using Streams for Large Responses
|
|
1036
|
+
|
|
1037
|
+
```js
|
|
1038
|
+
// ANTI-PATTERN: Buffers entire 500 MB file
|
|
1039
|
+
app.get('/download/:file', async (req, res) => {
|
|
1040
|
+
const data = await fs.promises.readFile(`/uploads/${req.params.file}`);
|
|
1041
|
+
res.send(data); // 500 MB in memory per concurrent download
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
// FIX: Stream the file
|
|
1045
|
+
app.get('/download/:file', (req, res) => {
|
|
1046
|
+
const stream = fs.createReadStream(`/uploads/${req.params.file}`);
|
|
1047
|
+
stream.pipe(res);
|
|
1048
|
+
stream.on('error', (err) => {
|
|
1049
|
+
res.status(404).end('File not found');
|
|
1050
|
+
});
|
|
1051
|
+
});
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
**Impact:** 10 concurrent 500 MB downloads = 5 GB RAM with buffering vs ~10 MB
|
|
1055
|
+
with streaming.
|
|
1056
|
+
|
|
1057
|
+
### Anti-Pattern 5: Missing Connection Pooling
|
|
1058
|
+
|
|
1059
|
+
```js
|
|
1060
|
+
// ANTI-PATTERN: New connection per query
|
|
1061
|
+
app.get('/user/:id', async (req, res) => {
|
|
1062
|
+
const client = new pg.Client(connectionString);
|
|
1063
|
+
await client.connect(); // 10-50ms overhead each time
|
|
1064
|
+
const result = await client.query(
|
|
1065
|
+
'SELECT * FROM users WHERE id = $1', [req.params.id]
|
|
1066
|
+
);
|
|
1067
|
+
await client.end();
|
|
1068
|
+
res.json(result.rows[0]);
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
// FIX: Use a shared pool
|
|
1072
|
+
const pool = new pg.Pool({ max: 20, connectionString });
|
|
1073
|
+
app.get('/user/:id', async (req, res) => {
|
|
1074
|
+
const result = await pool.query(
|
|
1075
|
+
'SELECT * FROM users WHERE id = $1', [req.params.id]
|
|
1076
|
+
);
|
|
1077
|
+
res.json(result.rows[0]);
|
|
1078
|
+
});
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
### Anti-Pattern 6: Large JSON.parse on Main Thread
|
|
1082
|
+
|
|
1083
|
+
```js
|
|
1084
|
+
// ANTI-PATTERN: 50 MB JSON payload parsed synchronously (~50ms block)
|
|
1085
|
+
app.post('/import', express.json({ limit: '100mb' }), (req, res) => {
|
|
1086
|
+
processData(req.body); // JSON.parse already blocked for 50ms
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
// FIX: Stream JSON parsing with stream-json
|
|
1090
|
+
const { parser } = require('stream-json');
|
|
1091
|
+
const { streamArray } = require('stream-json/streamers/StreamArray');
|
|
1092
|
+
|
|
1093
|
+
app.post('/import', (req, res) => {
|
|
1094
|
+
const jsonPipeline = req
|
|
1095
|
+
.pipe(parser())
|
|
1096
|
+
.pipe(streamArray());
|
|
1097
|
+
|
|
1098
|
+
jsonPipeline.on('data', ({ value }) => processRecord(value));
|
|
1099
|
+
jsonPipeline.on('end', () => res.json({ status: 'done' }));
|
|
1100
|
+
jsonPipeline.on('error', (err) => res.status(400).json({ error: err.message }));
|
|
1101
|
+
});
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
---
|
|
1105
|
+
|
|
1106
|
+
## 10. Benchmarking Methodology
|
|
1107
|
+
|
|
1108
|
+
### Tools
|
|
1109
|
+
|
|
1110
|
+
**autocannon** (Node.js native, recommended for Node apps):
|
|
1111
|
+
|
|
1112
|
+
```bash
|
|
1113
|
+
npm install -g autocannon
|
|
1114
|
+
|
|
1115
|
+
# Basic benchmark: 100 connections, 30 seconds
|
|
1116
|
+
autocannon -c 100 -d 30 http://localhost:3000/api
|
|
1117
|
+
|
|
1118
|
+
# With pipelining (10 requests per connection before waiting)
|
|
1119
|
+
autocannon -c 100 -d 30 -p 10 http://localhost:3000/api
|
|
1120
|
+
|
|
1121
|
+
# Output includes: latency histogram, req/s, throughput
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
**wrk** (C-based, higher load generation capacity):
|
|
1125
|
+
|
|
1126
|
+
```bash
|
|
1127
|
+
# Install: brew install wrk (macOS) or apt install wrk (Ubuntu)
|
|
1128
|
+
|
|
1129
|
+
# 4 threads, 100 connections, 30 seconds
|
|
1130
|
+
wrk -t4 -c100 -d30s http://localhost:3000/api
|
|
1131
|
+
|
|
1132
|
+
# With Lua script for POST requests
|
|
1133
|
+
wrk -t4 -c100 -d30s -s post.lua http://localhost:3000/api
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
**Comparison:**
|
|
1137
|
+
|
|
1138
|
+
| Feature | autocannon | wrk |
|
|
1139
|
+
|------------------------|-------------------|-----------------|
|
|
1140
|
+
| Language | JavaScript | C |
|
|
1141
|
+
| Max load (single core) | ~54k req/s | ~100k+ req/s |
|
|
1142
|
+
| HTTP pipelining | Yes | No |
|
|
1143
|
+
| Scripting | JS (programmatic) | Lua |
|
|
1144
|
+
| Latency histogram | Built-in | With HdrHistogram|
|
|
1145
|
+
| Installation | npm install -g | System package |
|
|
1146
|
+
|
|
1147
|
+
Both tools saturate a typical Node.js HTTP server at ~41k req/s on a single core.
|
|
1148
|
+
Source: [autocannon on GitHub](https://github.com/mcollina/autocannon)
|
|
1149
|
+
|
|
1150
|
+
### Benchmarking Best Practices
|
|
1151
|
+
|
|
1152
|
+
1. **Warm up first**: Run 5-10 seconds of load before measuring to allow V8's
|
|
1153
|
+
TurboFan optimizer to compile hot functions.
|
|
1154
|
+
|
|
1155
|
+
2. **Isolate variables**: Change one thing at a time. Benchmark before and after.
|
|
1156
|
+
|
|
1157
|
+
3. **Use percentiles, not averages**: p50 (median), p95, and p99 latency reveal
|
|
1158
|
+
tail latency that averages hide.
|
|
1159
|
+
|
|
1160
|
+
4. **Match production conditions**: Test with realistic payload sizes, database
|
|
1161
|
+
state, and connection counts.
|
|
1162
|
+
|
|
1163
|
+
5. **Multiple runs**: Run each benchmark 3-5 times and report median results
|
|
1164
|
+
to account for system noise.
|
|
1165
|
+
|
|
1166
|
+
6. **Disable CPU frequency scaling** during benchmarks:
|
|
1167
|
+
```bash
|
|
1168
|
+
# Linux: Set CPU governor to performance
|
|
1169
|
+
echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
### Complete Before/After Benchmark Example
|
|
1173
|
+
|
|
1174
|
+
**Scenario:** Express API serving user profiles with database lookup.
|
|
1175
|
+
|
|
1176
|
+
```js
|
|
1177
|
+
// BEFORE: No pooling, sync config read, no Keep-Alive
|
|
1178
|
+
const express = require('express');
|
|
1179
|
+
const pg = require('pg');
|
|
1180
|
+
const fs = require('fs');
|
|
1181
|
+
const app = express();
|
|
1182
|
+
|
|
1183
|
+
app.get('/user/:id', async (req, res) => {
|
|
1184
|
+
const config = JSON.parse(fs.readFileSync('config.json', 'utf8'));
|
|
1185
|
+
const client = new pg.Client(config.db);
|
|
1186
|
+
await client.connect();
|
|
1187
|
+
const result = await client.query(
|
|
1188
|
+
'SELECT * FROM users WHERE id = $1', [req.params.id]
|
|
1189
|
+
);
|
|
1190
|
+
await client.end();
|
|
1191
|
+
res.json(result.rows[0]);
|
|
1192
|
+
});
|
|
1193
|
+
app.listen(3000);
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1196
|
+
```bash
|
|
1197
|
+
# BEFORE benchmark
|
|
1198
|
+
autocannon -c 100 -d 30 http://localhost:3000/user/42
|
|
1199
|
+
# Result: 420 req/s, p99: 890ms, memory: 180 MB
|
|
1200
|
+
```
|
|
1201
|
+
|
|
1202
|
+
```js
|
|
1203
|
+
// AFTER: Connection pool, cached config, Keep-Alive
|
|
1204
|
+
const express = require('express');
|
|
1205
|
+
const pg = require('pg');
|
|
1206
|
+
const fs = require('fs');
|
|
1207
|
+
const app = express();
|
|
1208
|
+
|
|
1209
|
+
// Read config once at startup (sync is fine here)
|
|
1210
|
+
const config = JSON.parse(fs.readFileSync('config.json', 'utf8'));
|
|
1211
|
+
const pool = new pg.Pool({ ...config.db, max: 20, min: 5 });
|
|
1212
|
+
|
|
1213
|
+
app.get('/user/:id', async (req, res) => {
|
|
1214
|
+
const result = await pool.query(
|
|
1215
|
+
'SELECT * FROM users WHERE id = $1', [req.params.id]
|
|
1216
|
+
);
|
|
1217
|
+
res.json(result.rows[0]);
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
const server = app.listen(3000);
|
|
1221
|
+
server.keepAliveTimeout = 65000; // Slightly higher than typical LB timeout (60s)
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
```bash
|
|
1225
|
+
# AFTER benchmark
|
|
1226
|
+
autocannon -c 100 -d 30 http://localhost:3000/user/42
|
|
1227
|
+
# Result: 6,800 req/s, p99: 22ms, memory: 95 MB
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
**Improvement: 16.2x throughput, 40x latency reduction, 47% memory reduction.**
|
|
1231
|
+
|
|
1232
|
+
### Recommended V8 Flags for Production
|
|
1233
|
+
|
|
1234
|
+
```bash
|
|
1235
|
+
node \
|
|
1236
|
+
--max-old-space-size=4096 \
|
|
1237
|
+
--max-semi-space-size=64 \
|
|
1238
|
+
--trace-warnings \
|
|
1239
|
+
server.js
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
Additional flags for specific scenarios:
|
|
1243
|
+
|
|
1244
|
+
```bash
|
|
1245
|
+
# Expose GC for manual triggering (testing only, not production)
|
|
1246
|
+
--expose-gc
|
|
1247
|
+
|
|
1248
|
+
# Enable source maps for production error stacks
|
|
1249
|
+
--enable-source-maps
|
|
1250
|
+
|
|
1251
|
+
# Increase UV threadpool for DNS/file-heavy workloads
|
|
1252
|
+
UV_THREADPOOL_SIZE=16 node server.js
|
|
1253
|
+
```
|
|
1254
|
+
|
|
1255
|
+
---
|
|
1256
|
+
|
|
1257
|
+
## Quick Reference: Performance Checklist
|
|
1258
|
+
|
|
1259
|
+
```
|
|
1260
|
+
[ ] No synchronous I/O in request handlers (readFileSync, writeFileSync)
|
|
1261
|
+
[ ] CPU-bound work offloaded to worker threads or async alternatives
|
|
1262
|
+
[ ] Database connections pooled (pg.Pool, mysql2.createPool)
|
|
1263
|
+
[ ] HTTP Keep-Alive enabled for outbound requests
|
|
1264
|
+
[ ] Streams used for files > 1 MB (createReadStream, pipeline)
|
|
1265
|
+
[ ] JSON parsing streamed for payloads > 10 MB
|
|
1266
|
+
[ ] Event listeners removed on disconnect (removeListener in 'close')
|
|
1267
|
+
[ ] Promise chains use loops, not recursion for retries
|
|
1268
|
+
[ ] --max-semi-space-size=64 set for high-throughput servers
|
|
1269
|
+
[ ] --max-old-space-size set appropriately for available RAM
|
|
1270
|
+
[ ] UV_THREADPOOL_SIZE increased if >4 concurrent DNS lookups expected
|
|
1271
|
+
[ ] Event loop delay monitored (monitorEventLoopDelay or blocked library)
|
|
1272
|
+
[ ] Memory usage tracked over time (process.memoryUsage -> Prometheus)
|
|
1273
|
+
[ ] Caching layer for frequently-read, rarely-changed data
|
|
1274
|
+
[ ] Profiling run under load before deployment (clinic flame or 0x)
|
|
1275
|
+
```
|
|
1276
|
+
|
|
1277
|
+
---
|
|
1278
|
+
|
|
1279
|
+
## Sources
|
|
1280
|
+
|
|
1281
|
+
- [NodeSource - State of Node.js Performance 2024](https://nodesource.com/blog/State-of-Nodejs-Performance-2024)
|
|
1282
|
+
- [NearForm - Impact of --max-semi-space-size on GC Efficiency](https://nearform.com/digital-community/optimising-node-js-applications-the-impact-of-max-semi-space-size-on-garbage-collection-efficiency/)
|
|
1283
|
+
- [Platformatic - V8 Memory Management and GC Tuning](https://blog.platformatic.dev/optimizing-nodejs-performance-v8-memory-management-and-gc-tuning)
|
|
1284
|
+
- [Akamas - Tuning Node.js and V8 to Unlock 2x Performance](https://akamas.io/resources/tuning-nodejs-v8-performance-efficiency/)
|
|
1285
|
+
- [Alibaba Cloud - Node.js GC Optimization](https://www.alibabacloud.com/blog/better-node-application-performance-through-gc-optimization_595119)
|
|
1286
|
+
- [Node.js Docs - Don't Block the Event Loop](https://nodejs.org/en/learn/asynchronous-work/dont-block-the-event-loop)
|
|
1287
|
+
- [Node.js Docs - Profiling Node.js Applications](https://nodejs.org/en/learn/getting-started/profiling)
|
|
1288
|
+
- [Node.js Docs - Flame Graphs](https://nodejs.org/en/learn/diagnostics/flame-graphs)
|
|
1289
|
+
- [Node.js Docs - Using Heap Snapshot](https://nodejs.org/en/learn/diagnostics/memory/using-heap-snapshot)
|
|
1290
|
+
- [Node.js Docs - Tracing Garbage Collection](https://nodejs.org/en/learn/diagnostics/memory/using-gc-traces)
|
|
1291
|
+
- [Ashby Engineering - Detecting Event Loop Blockers](https://www.ashbyhq.com/blog/engineering/detecting-event-loop-blockers)
|
|
1292
|
+
- [Sentry - Event Loop Block Detection](https://docs.sentry.io/platforms/javascript/guides/node/configuration/event-loop-block/)
|
|
1293
|
+
- [StackInsight - Blocking I/O Empirical Study](https://stackinsight.dev/blog/blocking-io-empirical-study)
|
|
1294
|
+
- [Paige Niedringhaus - Streams For the Win](https://www.paigeniedringhaus.com/blog/streams-for-the-win-a-performance-comparison-of-node-js-methods-for-reading-large-datasets-pt-2/)
|
|
1295
|
+
- [Stack Overflow - Database Connection Pooling](https://stackoverflow.blog/2020/10/14/improve-database-performance-with-connection-pooling/)
|
|
1296
|
+
- [Lob Engineering - Use HTTP Keep-Alive](https://www.lob.com/blog/use-http-keep-alive)
|
|
1297
|
+
- [HTTP Toolkit - Fixing DNS in Node.js](https://httptoolkit.com/blog/configuring-nodejs-dns/)
|
|
1298
|
+
- [NearForm - Introducing Clinic.js](https://www.nearform.com/blog/introducing-node-clinic-a-performance-toolkit-for-node-js-developers/)
|
|
1299
|
+
- [Cribl - Promise Chaining Memory Leak Pattern](https://cribl.io/blog/promise-chaining-memory-leak/)
|
|
1300
|
+
- [RisingStack - Node.js Garbage Collection Explained](https://blog.risingstack.com/node-js-at-scale-node-js-garbage-collection/)
|
|
1301
|
+
- [AppSignal - CPU-bound Tasks in Node.js](https://blog.appsignal.com/2024/01/17/dealing-with-cpu-bound-tasks-in-nodejs.html)
|
|
1302
|
+
- [DEV Community - Benchmarking Worker Threads](https://dev.to/dhwaneetbhatt/benchmarking-nodejs-worker-threads-5c9b)
|
|
1303
|
+
- [0x on GitHub](https://github.com/davidmarkclements/0x)
|
|
1304
|
+
- [autocannon on GitHub](https://github.com/mcollina/autocannon)
|
|
1305
|
+
- [tj/node-blocked on GitHub](https://github.com/tj/node-blocked)
|
|
1306
|
+
- [naugtur/blocked-at on GitHub](https://github.com/naugtur/blocked-at)
|
|
1307
|
+
- [mapbox/flamebearer on GitHub](https://github.com/mapbox/flamebearer)
|