feat: add fastify poolifier integration example
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 11 Aug 2023 16:27:47 +0000 (18:27 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 11 Aug 2023 16:27:47 +0000 (18:27 +0200)
Reference #790

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
15 files changed:
.eslintrc.js
.vscode/settings.json
CHANGELOG.md
README.md
examples/typescript/http-server-pool/express/src/main.ts
examples/typescript/http-server-pool/express/src/worker.ts
examples/typescript/http-server-pool/fastify/@types/fastify/index.d.ts [new file with mode: 0644]
examples/typescript/http-server-pool/fastify/package.json [new file with mode: 0644]
examples/typescript/http-server-pool/fastify/pnpm-lock.yaml [new file with mode: 0644]
examples/typescript/http-server-pool/fastify/requests.sh [new file with mode: 0755]
examples/typescript/http-server-pool/fastify/src/fastify-poolifier.ts [new file with mode: 0644]
examples/typescript/http-server-pool/fastify/src/main.ts [new file with mode: 0644]
examples/typescript/http-server-pool/fastify/src/types.ts [new file with mode: 0644]
examples/typescript/http-server-pool/fastify/src/worker.ts [new file with mode: 0644]
examples/typescript/http-server-pool/fastify/tsconfig.json [new file with mode: 0644]

index c5a0992f8d22a7f445c28a80350aadeb53ad9e6f..eb288bd1672c348d3cb2d1e9c80794f59105add3 100644 (file)
@@ -57,7 +57,9 @@ module.exports = defineConfig({
           'enum',
           'errored',
           'esm',
+          'fastify',
           'fibonacci',
+          'fp',
           'fs',
           'inheritDoc',
           'jsdoc',
index 14e935036539eb7c1ff6cc78d07d8cb87861ebe3..5be1f55aa000422534b9ae3b07853338842b6792 100644 (file)
@@ -13,6 +13,7 @@
     "commitlint",
     "Dependabot",
     "eventloop",
+    "Fastify",
     "FOSS",
     "Gitter",
     "inheritDoc",
index 8761941ed05c7daf1833b0024ccae658eb9a27b9..e4840977a6a063237f2cb029be3752c99a66d83d 100644 (file)
@@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 
 - HTTP client pool examples: fetch, node-fetch and axios with multiple task functions.
-- HTTP server pool examples: express.
+- HTTP server pool examples: express, fastify.
 
 ## [2.6.22] - 2023-08-10
 
index 546cb9ae4f698537fe5cd5603e5e5321175bab60..d56bb1878e6dec25e35b37de7b1dbe59251028ba 100644 (file)
--- a/README.md
+++ b/README.md
@@ -148,6 +148,7 @@ You can do the same with the classes _ClusterWorker_, _FixedClusterPool_ and _Dy
   - [HTTP client pool](./examples/typescript/http-client-pool/)
   - [HTTP server pool](./examples/typescript/http-server-pool/)
     - [Express](./examples/typescript/http-server-pool/express/)
+    - [Fastify](./examples/typescript/http-server-pool/fastify/)
 
 Remember that workers can only send and receive structured-cloneable data.
 
index 4f94a83ef2a46af620fed40ac57fcf87dba5cde7..02e139399259b4272b958ba41bb9420aef796f3c 100644 (file)
@@ -27,8 +27,13 @@ expressApp.all('/api/echo', (req: Request, res: Response) => {
     .catch(emptyFunction)
 })
 
-expressApp.listen(port, () => {
-  console.info(
-    `⚡️[express server]: Express server is started at http://localhost:${port}/`
-  )
-})
+try {
+  expressApp.listen(port, () => {
+    console.info(
+      `⚡️[express server]: Express server is started at http://localhost:${port}/`
+    )
+  })
+} catch (err) {
+  console.error(err)
+  process.exit(1)
+}
index 65fc7437a0c98f6708799fcc30f378f1e0b20a9d..f006ad48217ba2a935498a9468a43a4ab5341bf4 100644 (file)
@@ -1,14 +1,20 @@
 import { ThreadWorker } from 'poolifier'
 import { type WorkerData, type WorkerResponse } from './types.js'
 
-class RequestHandlerWorker extends ThreadWorker<WorkerData, WorkerResponse> {
+class RequestHandlerWorker<
+  Data extends WorkerData,
+  Response extends WorkerResponse
+> extends ThreadWorker<Data, Response> {
   public constructor () {
     super({
-      echo: (workerData?: WorkerData) => {
-        return workerData as WorkerResponse
+      echo: (workerData?: Data) => {
+        return workerData as unknown as Response
       }
     })
   }
 }
 
-export const requestHandlerWorker = new RequestHandlerWorker()
+export const requestHandlerWorker = new RequestHandlerWorker<
+WorkerData,
+WorkerResponse
+>()
diff --git a/examples/typescript/http-server-pool/fastify/@types/fastify/index.d.ts b/examples/typescript/http-server-pool/fastify/@types/fastify/index.d.ts
new file mode 100644 (file)
index 0000000..0e43ad2
--- /dev/null
@@ -0,0 +1,10 @@
+import type * as fastify from 'fastify'
+import { type DynamicThreadPool } from 'poolifier'
+import { type WorkerData, type WorkerResponse } from '../../src/types.ts'
+
+declare module 'fastify' {
+  export interface FastifyInstance extends fastify.FastifyInstance {
+    pool: DynamicThreadPool<WorkerData, WorkerResponse>
+    execute: (data?: WorkerData, name?: string) => Promise<WorkerResponse>
+  }
+}
diff --git a/examples/typescript/http-server-pool/fastify/package.json b/examples/typescript/http-server-pool/fastify/package.json
new file mode 100644 (file)
index 0000000..c42ea0c
--- /dev/null
@@ -0,0 +1,30 @@
+{
+  "$schema": "https://json.schemastore.org/package",
+  "name": "express-request-pool",
+  "version": "1.0.0",
+  "description": "Express request pool",
+  "main": "dist/main.js",
+  "type": "module",
+  "volta": {
+    "node": "20.5.1",
+    "pnpm": "8.6.12"
+  },
+  "scripts": {
+    "build": "pnpm build:clean && tsc",
+    "build:clean": "tsc --build --clean",
+    "start": "node dist/main.js",
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "fastify": "^4.21.0",
+    "fastify-plugin": "^4.5.1",
+    "poolifier": "^2.6.22"
+  },
+  "devDependencies": {
+    "@types/node": "^20.4.9",
+    "typescript": "^5.1.6"
+  }
+}
diff --git a/examples/typescript/http-server-pool/fastify/pnpm-lock.yaml b/examples/typescript/http-server-pool/fastify/pnpm-lock.yaml
new file mode 100644 (file)
index 0000000..181c4d2
--- /dev/null
@@ -0,0 +1,431 @@
+lockfileVersion: '6.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+dependencies:
+  fastify:
+    specifier: ^4.21.0
+    version: 4.21.0
+  fastify-plugin:
+    specifier: ^4.5.1
+    version: 4.5.1
+  poolifier:
+    specifier: ^2.6.22
+    version: 2.6.22
+
+devDependencies:
+  '@types/node':
+    specifier: ^20.4.9
+    version: 20.4.9
+  typescript:
+    specifier: ^5.1.6
+    version: 5.1.6
+
+packages:
+
+  /@fastify/ajv-compiler@3.5.0:
+    resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==}
+    dependencies:
+      ajv: 8.12.0
+      ajv-formats: 2.1.1(ajv@8.12.0)
+      fast-uri: 2.2.0
+    dev: false
+
+  /@fastify/deepmerge@1.3.0:
+    resolution: {integrity: sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==}
+    dev: false
+
+  /@fastify/error@3.3.0:
+    resolution: {integrity: sha512-dj7vjIn1Ar8sVXj2yAXiMNCJDmS9MQ9XMlIecX2dIzzhjSHCyKo4DdXjXMs7wKW2kj6yvVRSpuQjOZ3YLrh56w==}
+    dev: false
+
+  /@fastify/fast-json-stringify-compiler@4.3.0:
+    resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==}
+    dependencies:
+      fast-json-stringify: 5.8.0
+    dev: false
+
+  /@types/node@20.4.9:
+    resolution: {integrity: sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==}
+    dev: true
+
+  /abort-controller@3.0.0:
+    resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
+    engines: {node: '>=6.5'}
+    dependencies:
+      event-target-shim: 5.0.1
+    dev: false
+
+  /abstract-logging@2.0.1:
+    resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
+    dev: false
+
+  /ajv-formats@2.1.1(ajv@8.12.0):
+    resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
+    peerDependencies:
+      ajv: ^8.0.0
+    peerDependenciesMeta:
+      ajv:
+        optional: true
+    dependencies:
+      ajv: 8.12.0
+    dev: false
+
+  /ajv@8.12.0:
+    resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
+    dependencies:
+      fast-deep-equal: 3.1.3
+      json-schema-traverse: 1.0.0
+      require-from-string: 2.0.2
+      uri-js: 4.4.1
+    dev: false
+
+  /archy@1.0.0:
+    resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==}
+    dev: false
+
+  /atomic-sleep@1.0.0:
+    resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
+    engines: {node: '>=8.0.0'}
+    dev: false
+
+  /avvio@8.2.1:
+    resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==}
+    dependencies:
+      archy: 1.0.0
+      debug: 4.3.4
+      fastq: 1.15.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: false
+
+  /base64-js@1.5.1:
+    resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+    dev: false
+
+  /buffer@6.0.3:
+    resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
+    dependencies:
+      base64-js: 1.5.1
+      ieee754: 1.2.1
+    dev: false
+
+  /cookie@0.5.0:
+    resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
+    engines: {node: '>= 0.6'}
+    dev: false
+
+  /debug@4.3.4:
+    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.1.2
+    dev: false
+
+  /event-target-shim@5.0.1:
+    resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
+    engines: {node: '>=6'}
+    dev: false
+
+  /events@3.3.0:
+    resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+    engines: {node: '>=0.8.x'}
+    dev: false
+
+  /fast-content-type-parse@1.0.0:
+    resolution: {integrity: sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA==}
+    dev: false
+
+  /fast-decode-uri-component@1.0.1:
+    resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
+    dev: false
+
+  /fast-deep-equal@3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+    dev: false
+
+  /fast-json-stringify@5.8.0:
+    resolution: {integrity: sha512-VVwK8CFMSALIvt14U8AvrSzQAwN/0vaVRiFFUVlpnXSnDGrSkOAO5MtzyN8oQNjLd5AqTW5OZRgyjoNuAuR3jQ==}
+    dependencies:
+      '@fastify/deepmerge': 1.3.0
+      ajv: 8.12.0
+      ajv-formats: 2.1.1(ajv@8.12.0)
+      fast-deep-equal: 3.1.3
+      fast-uri: 2.2.0
+      rfdc: 1.3.0
+    dev: false
+
+  /fast-querystring@1.1.2:
+    resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==}
+    dependencies:
+      fast-decode-uri-component: 1.0.1
+    dev: false
+
+  /fast-redact@3.3.0:
+    resolution: {integrity: sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==}
+    engines: {node: '>=6'}
+    dev: false
+
+  /fast-uri@2.2.0:
+    resolution: {integrity: sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==}
+    dev: false
+
+  /fastify-plugin@4.5.1:
+    resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==}
+    dev: false
+
+  /fastify@4.21.0:
+    resolution: {integrity: sha512-tsu4bcwE4HetxqW8prA5fbC9bKHMYDp7jGEDWyzK1l90a3uOaLoIcQbdGcWeODNLVJviQnzh1wvIjTZE3MJFEg==}
+    dependencies:
+      '@fastify/ajv-compiler': 3.5.0
+      '@fastify/error': 3.3.0
+      '@fastify/fast-json-stringify-compiler': 4.3.0
+      abstract-logging: 2.0.1
+      avvio: 8.2.1
+      fast-content-type-parse: 1.0.0
+      fast-json-stringify: 5.8.0
+      find-my-way: 7.6.2
+      light-my-request: 5.10.0
+      pino: 8.15.0
+      process-warning: 2.2.0
+      proxy-addr: 2.0.7
+      rfdc: 1.3.0
+      secure-json-parse: 2.7.0
+      semver: 7.5.4
+      tiny-lru: 11.0.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: false
+
+  /fastq@1.15.0:
+    resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
+    dependencies:
+      reusify: 1.0.4
+    dev: false
+
+  /find-my-way@7.6.2:
+    resolution: {integrity: sha512-0OjHn1b1nCX3eVbm9ByeEHiscPYiHLfhei1wOUU9qffQkk98wE0Lo8VrVYfSGMgnSnDh86DxedduAnBf4nwUEw==}
+    engines: {node: '>=14'}
+    dependencies:
+      fast-deep-equal: 3.1.3
+      fast-querystring: 1.1.2
+      safe-regex2: 2.0.0
+    dev: false
+
+  /forwarded@0.2.0:
+    resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
+    engines: {node: '>= 0.6'}
+    dev: false
+
+  /ieee754@1.2.1:
+    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+    dev: false
+
+  /ipaddr.js@1.9.1:
+    resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
+    engines: {node: '>= 0.10'}
+    dev: false
+
+  /json-schema-traverse@1.0.0:
+    resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+    dev: false
+
+  /light-my-request@5.10.0:
+    resolution: {integrity: sha512-ZU2D9GmAcOUculTTdH9/zryej6n8TzT+fNGdNtm6SDp5MMMpHrJJkvAdE3c6d8d2chE9i+a//dS9CWZtisknqA==}
+    dependencies:
+      cookie: 0.5.0
+      process-warning: 2.2.0
+      set-cookie-parser: 2.6.0
+    dev: false
+
+  /lru-cache@6.0.0:
+    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+    engines: {node: '>=10'}
+    dependencies:
+      yallist: 4.0.0
+    dev: false
+
+  /ms@2.1.2:
+    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+    dev: false
+
+  /on-exit-leak-free@2.1.0:
+    resolution: {integrity: sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==}
+    dev: false
+
+  /pino-abstract-transport@1.0.0:
+    resolution: {integrity: sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==}
+    dependencies:
+      readable-stream: 4.4.2
+      split2: 4.2.0
+    dev: false
+
+  /pino-std-serializers@6.2.2:
+    resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==}
+    dev: false
+
+  /pino@8.15.0:
+    resolution: {integrity: sha512-olUADJByk4twxccmAxb1RiGKOSvddHugCV3wkqjyv+3Sooa2KLrmXrKEWOKi0XPCLasRR5jBXxioE1jxUa4KzQ==}
+    hasBin: true
+    dependencies:
+      atomic-sleep: 1.0.0
+      fast-redact: 3.3.0
+      on-exit-leak-free: 2.1.0
+      pino-abstract-transport: 1.0.0
+      pino-std-serializers: 6.2.2
+      process-warning: 2.2.0
+      quick-format-unescaped: 4.0.4
+      real-require: 0.2.0
+      safe-stable-stringify: 2.4.3
+      sonic-boom: 3.3.0
+      thread-stream: 2.4.0
+    dev: false
+
+  /poolifier@2.6.22:
+    resolution: {integrity: sha512-0pGU1nG8jVEQUb2j1kkiEQ5TdJyHP3a9zFVxF7Q23xHiGM2hpw9BpWvjP0kvE6LmCx0R8ezFDwaXf9nDUOA9gQ==}
+    engines: {node: '>=16.14.0', pnpm: '>=8.6.0'}
+    requiresBuild: true
+    dev: false
+
+  /process-warning@2.2.0:
+    resolution: {integrity: sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==}
+    dev: false
+
+  /process@0.11.10:
+    resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
+    engines: {node: '>= 0.6.0'}
+    dev: false
+
+  /proxy-addr@2.0.7:
+    resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
+    engines: {node: '>= 0.10'}
+    dependencies:
+      forwarded: 0.2.0
+      ipaddr.js: 1.9.1
+    dev: false
+
+  /punycode@2.3.0:
+    resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
+    engines: {node: '>=6'}
+    dev: false
+
+  /quick-format-unescaped@4.0.4:
+    resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
+    dev: false
+
+  /readable-stream@4.4.2:
+    resolution: {integrity: sha512-Lk/fICSyIhodxy1IDK2HazkeGjSmezAWX2egdtJnYhtzKEsBPJowlI6F6LPb5tqIQILrMbx22S5o3GuJavPusA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      abort-controller: 3.0.0
+      buffer: 6.0.3
+      events: 3.3.0
+      process: 0.11.10
+      string_decoder: 1.3.0
+    dev: false
+
+  /real-require@0.2.0:
+    resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
+    engines: {node: '>= 12.13.0'}
+    dev: false
+
+  /require-from-string@2.0.2:
+    resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+    engines: {node: '>=0.10.0'}
+    dev: false
+
+  /ret@0.2.2:
+    resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==}
+    engines: {node: '>=4'}
+    dev: false
+
+  /reusify@1.0.4:
+    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+    dev: false
+
+  /rfdc@1.3.0:
+    resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
+    dev: false
+
+  /safe-buffer@5.2.1:
+    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+    dev: false
+
+  /safe-regex2@2.0.0:
+    resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==}
+    dependencies:
+      ret: 0.2.2
+    dev: false
+
+  /safe-stable-stringify@2.4.3:
+    resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==}
+    engines: {node: '>=10'}
+    dev: false
+
+  /secure-json-parse@2.7.0:
+    resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
+    dev: false
+
+  /semver@7.5.4:
+    resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dependencies:
+      lru-cache: 6.0.0
+    dev: false
+
+  /set-cookie-parser@2.6.0:
+    resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==}
+    dev: false
+
+  /sonic-boom@3.3.0:
+    resolution: {integrity: sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==}
+    dependencies:
+      atomic-sleep: 1.0.0
+    dev: false
+
+  /split2@4.2.0:
+    resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
+    engines: {node: '>= 10.x'}
+    dev: false
+
+  /string_decoder@1.3.0:
+    resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+    dependencies:
+      safe-buffer: 5.2.1
+    dev: false
+
+  /thread-stream@2.4.0:
+    resolution: {integrity: sha512-xZYtOtmnA63zj04Q+F9bdEay5r47bvpo1CaNqsKi7TpoJHcotUez8Fkfo2RJWpW91lnnaApdpRbVwCWsy+ifcw==}
+    dependencies:
+      real-require: 0.2.0
+    dev: false
+
+  /tiny-lru@11.0.1:
+    resolution: {integrity: sha512-iNgFugVuQgBKrqeO/mpiTTgmBsTP0WL6yeuLfLs/Ctf0pI/ixGqIRm8sDCwMcXGe9WWvt2sGXI5mNqZbValmJg==}
+    engines: {node: '>=12'}
+    dev: false
+
+  /typescript@5.1.6:
+    resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+    dev: true
+
+  /uri-js@4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+    dependencies:
+      punycode: 2.3.0
+    dev: false
+
+  /yallist@4.0.0:
+    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+    dev: false
diff --git a/examples/typescript/http-server-pool/fastify/requests.sh b/examples/typescript/http-server-pool/fastify/requests.sh
new file mode 100755 (executable)
index 0000000..e5ad31b
--- /dev/null
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+set -e
+
+for ((request=1;request<=60;request++))
+do
+  time curl -i -H "Content-Type: application/json" -X POST -d '{"key1":"value1", "key2":"value2"}' http://localhost:8080/api/echo
+done
diff --git a/examples/typescript/http-server-pool/fastify/src/fastify-poolifier.ts b/examples/typescript/http-server-pool/fastify/src/fastify-poolifier.ts
new file mode 100644 (file)
index 0000000..1ced051
--- /dev/null
@@ -0,0 +1,37 @@
+import { DynamicThreadPool } from 'poolifier'
+import { type FastifyPluginCallback } from 'fastify'
+import fp from 'fastify-plugin'
+import {
+  type FastifyPoolifierOptions,
+  type WorkerData,
+  type WorkerResponse
+} from './types.js'
+
+const fastifyPoolifierPlugin: FastifyPluginCallback<FastifyPoolifierOptions> = (
+  fastify,
+  options,
+  done
+): void => {
+  const pool = new DynamicThreadPool<WorkerData, WorkerResponse>(
+    options.minWorkers,
+    options.maxWorkers,
+    options.workerFile,
+    options
+  )
+  if (!fastify.hasDecorator('pool')) {
+    fastify.decorate('pool', pool)
+  }
+  if (!fastify.hasDecorator('execute')) {
+    fastify.decorate(
+      'execute',
+      async (data?: WorkerData, name?: string): Promise<WorkerResponse> =>
+        await pool.execute(data, name)
+    )
+  }
+  done()
+}
+
+export const fastifyPoolifier = fp(fastifyPoolifierPlugin, {
+  fastify: '4.x',
+  name: 'fastify-poolifier'
+})
diff --git a/examples/typescript/http-server-pool/fastify/src/main.ts b/examples/typescript/http-server-pool/fastify/src/main.ts
new file mode 100644 (file)
index 0000000..a681d02
--- /dev/null
@@ -0,0 +1,44 @@
+import { dirname, extname, join } from 'node:path'
+import { fileURLToPath } from 'node:url'
+import Fastify from 'fastify'
+import { availableParallelism } from 'poolifier'
+import { fastifyPoolifier } from './fastify-poolifier.js'
+
+const port = 8080
+const fastify = Fastify({
+  logger: true
+})
+
+const workerFile = join(
+  dirname(fileURLToPath(import.meta.url)),
+  `worker${extname(fileURLToPath(import.meta.url))}`
+)
+
+await fastify.register(fastifyPoolifier, {
+  workerFile,
+  minWorkers: 1,
+  maxWorkers: availableParallelism(),
+  enableTasksQueue: true,
+  tasksQueueOptions: {
+    concurrency: 8
+  },
+  errorHandler: (e: Error) => {
+    console.error(e)
+  }
+})
+
+fastify.all('/api/echo', async (request, reply) => {
+  await reply.send((await fastify.execute({ body: request.body }, 'echo')).body)
+})
+
+// fastify.get('/api/factorial/:number', async (request, reply) => {
+//   const { number } = request.params
+//   await reply.send((await fastify.execute({ body: { number } }, 'factorial')).body)
+// })
+
+try {
+  await fastify.listen({ port })
+} catch (err) {
+  fastify.log.error(err)
+  process.exit(1)
+}
diff --git a/examples/typescript/http-server-pool/fastify/src/types.ts b/examples/typescript/http-server-pool/fastify/src/types.ts
new file mode 100644 (file)
index 0000000..5c53f10
--- /dev/null
@@ -0,0 +1,19 @@
+import { type ThreadPoolOptions } from 'poolifier'
+
+export interface BodyPayload {
+  number?: number
+}
+
+export interface WorkerData<T = unknown> {
+  body: T
+}
+
+export interface WorkerResponse<T = unknown> {
+  body: T
+}
+
+export interface FastifyPoolifierOptions extends ThreadPoolOptions {
+  workerFile: string
+  maxWorkers: number
+  minWorkers: number
+}
diff --git a/examples/typescript/http-server-pool/fastify/src/worker.ts b/examples/typescript/http-server-pool/fastify/src/worker.ts
new file mode 100644 (file)
index 0000000..7589aa6
--- /dev/null
@@ -0,0 +1,36 @@
+import { ThreadWorker } from 'poolifier'
+import {
+  type BodyPayload,
+  type WorkerData,
+  type WorkerResponse
+} from './types.js'
+
+const factorial: (n: number) => number = n => {
+  if (n === 0) {
+    return 1
+  }
+  return factorial(n - 1) * n
+}
+
+class RequestHandlerWorker<
+  Data extends WorkerData<BodyPayload>,
+  Response extends WorkerResponse<BodyPayload>
+> extends ThreadWorker<Data, Response> {
+  public constructor () {
+    super({
+      echo: (workerData?: Data) => {
+        return workerData as unknown as Response
+      },
+      factorial: (workerData?: Data) => {
+        return {
+          body: { number: factorial(workerData?.body?.number as number) }
+        } as unknown as Response
+      }
+    })
+  }
+}
+
+export const requestHandlerWorker = new RequestHandlerWorker<
+WorkerData<BodyPayload>,
+WorkerResponse<BodyPayload>
+>()
diff --git a/examples/typescript/http-server-pool/fastify/tsconfig.json b/examples/typescript/http-server-pool/fastify/tsconfig.json
new file mode 100644 (file)
index 0000000..82728d1
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "$schema": "https://json.schemastore.org/tsconfig",
+  "compilerOptions": {
+    "target": "ES2022",
+    "module": "ES2022",
+    "moduleResolution": "Node16",
+    "rootDir": "./src",
+    "outDir": "./dist",
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "skipLibCheck": true
+  }
+}