---
sidebar_position: 3
title: Multithreading and Parallelism with Node.js and MotherDuck
sidebar_label: Node.js
description: Performance tuning via multithreading with multiple connections to MotherDuck with Node.js
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import useBaseUrl from '@docusaurus/useBaseUrl';

# Multithreading and parallelism with Node.js

For multiple long-lived connections to one or more databases in one or more MotherDuck accounts, you can use [connection pooling](#connection-pooling). Depending on the needs of your data application, you can use thread-based parallelism for improved performance, for example, if the queries are hybrid with CPU intensive work done locally. To enable thread-based parallelism, you can use [Node worker threads](https://nodejs.org/api/worker_threads.html#worker-threads) with one database connection in each thread.

If you need to run many concurrent read-only queries on the same MotherDuck account, you can use a [Read Scaling](/docs/key-tasks/authenticating-and-connecting-to-motherduck/read-scaling/) token.

## Connection pooling

<img src={useBaseUrl('/img/key-tasks/authenticating-and-connecting-to-motherduck/connection-pooling.png')} width="300" style={{maxWidth: '100%'}} />

If your application needs multiple read-only connections to a MotherDuck database, for example, to handle requests in a queue, you can use a Connection Pool. A Connection Pool keeps connections open for a longer period for efficient re-use, so you can avoid the overhead of creating a new database object for each query. The connections in your pool can connect to one database in the same MotherDuck account, or multiple databases in one or more accounts. To run concurrent read-only queries on the same MotherDuck account, you can use a [Read Scaling](/docs/key-tasks/authenticating-and-connecting-to-motherduck/read-scaling/) token.

For connection pools, we recommend using [generic-pool](https://www.npmjs.com/package/generic-pool) with [@duckdb/node-api](https://www.npmjs.com/package/@duckdb/node-api) and overriding the `release` function to delete a connection if it's been in use for too long to optimize resource usage.

First, let's create a file `md_connection_pool.js` to implement the connection pool class. Note that we are adding a new config option, `recycleTimeoutMillis`, that will help us recreate any connections (active or idle) that have been open for a given time. This is different from `idleTimeoutMillis`, which only destroys idle connections.

```javascript
import { DuckDBInstance } from "@duckdb/node-api";
import * as genericPool from "generic-pool";

export class RecyclingPool extends genericPool.Pool {
  constructor(Evictor, Deque, PriorityQueue, factory, options) {
    super(Evictor, Deque, PriorityQueue, factory, options);
    // New _config option for when to recycle a non-idle connection
    this._config['recycleTimeoutMillis'] = (typeof options.recycleTimeoutMillis == 'undefined') ? undefined : parseInt(options.recycleTimeoutMillis);
    this._config['motherduckToken'] = (typeof options.motherduckToken == 'undefined') ? undefined : options.motherduckToken;
    console.log('Creating a RecyclingPool');
  }

  release(resource) {
    const loan = this._resourceLoans.get(resource);
    const creationTime = typeof loan == 'undefined' ? 0 : loan.pooledResource.creationTime;

    // If the connection has been in use for longer than the recycleTimeoutMillis, then destroy it instead of releasing it back into the pool.
    // If that deletion brings the pool size below the min, a new connection will automatically be created within the destroy method.
    if (new Date(creationTime + this._config.recycleTimeoutMillis) <= new Date()) {
      return this.destroy(resource);
    }
    return super.release(resource);
  }
}

```

You can then create an `MDFactory` class to create the connection in the pool, and use it with `createRecyclingPool` (equivalent to the `createPool` function from `generic-pool`).

```javascript
export class MDFactory {
  constructor(opts) {
    this.opts = opts
  }
  async create() {
    console.log("Creating a connection");
    const instance = await DuckDBInstance.fromCache('md:my_db', {
      motherduck_token: this.opts.motherduckToken,
    });
    const connection = await instance.connect();
    // Run any connection initialization commands here
    // For example, you can set THREADS = 1 if you want to limit duckdb to run on a single thread
    await connection.run("SET THREADS='1';");
    return connection;
  }

  async destroy(connection) {
    console.log("Destroying a connection");
    return connection.close();
  }
};

export function createRecyclingPool(config) {
  const factory = new MDFactory(config);
  return new RecyclingPool(genericPool.DefaultEvictor, genericPool.Deque, genericPool.PriorityQueue, factory, config);
}
```

To try out the connection pool, you can create a file `md_connection_pool_test.js` that creates a `RecyclingPool` and submits a list of queries.

To create the pool instance, first set the configuration options specified by `generic-pool` and pass them to the `createRecyclingPool` function. You can find the list of options in the [docs](https://www.npmjs.com/package/generic-pool). Below are a few example values that we recommend for using with MotherDuck.

```javascript
import { createRecyclingPool } from "./md_connection_pool.js";

// If an idle eviction would bring us below the min pool size, a new connection is made after the eviction
const opts = {
  max: 10, 
  min: 3,
  // Background idle connection detruction process runs every evictionRunIntervalMillis
  // We don't want all connections to be evicted at the same time, so only destroy one at a time
  // Connection must be idle for softIdleTimeoutMillis before it is recycled.
  // (Additionally, we implemented recycleTimeoutMillis to also recycle active connections.)
  evictionRunIntervalMillis: 30000, 
  numTestsPerEvictionRun: 1,
  softIdleTimeoutMillis: 90000, 
  // Do not start to use a connection that is older than 20 minutes old. Recreate it first.
  // Set this higher than recycleTimeoutMillis below so that recycling will happen proactively rather than delay query execution.
  idleTimeoutMillis: 1200000, 
  // Before returning resource to pool, check if it has been in existence longer than this timeout and if so, destroy it. 
  // New connections will be added up to the min pool size during the destroy process, so this is proactive rather than reactive.
  recycleTimeoutMillis: 900000, 
  // We don't want all the connections to recycle at the same time, so let's randomize it slightly.
  // This number should be smaller than the recycleTimeoutMillis
  recycleTimeoutJitter: 60000,
  // This gets your MotherDuck token from an environment variable.
  motherduckToken: process.env.motherduck_token,
};

const myPool = createRecyclingPool(opts);
```

Then, you can use the pool to asynchronously acquire connections from the pool and run a list of queries.

```javascript
let promiseArray = [];
let queries = ["SELECT 42", "SELECT 'Hello World!'"];

for (let i=0; i < queries.length; i++) {
  // Promise is resolved once a resource becomes available
  console.log("Acquire connection from pool");
  promiseArray.push(myPool.acquire());
  promiseArray[i]
    .then(async function(client) {
      console.log("Starting query");
      const results = await client.all(queries[i]);
      console.log("Results: ", results[0]);

      await new Promise(r => setTimeout(r, 200)); // Delay for testing

      // Release the connection (or destroy if it exceeds recycleTimeoutMillis)
      myPool.release(client);
    })
    .catch(function(err) {
      console.log(err)
    });

}
```

You can create additional connection pools that connect to different MotherDuck databases by changing the MotherDuck token.

```javascript
const opts2 = { ...opts, motherduckToken: process.env.motherduck_token_2};
const myPool2 = createRecyclingPool(opts2);
```

To shutdown and stop using a pool, you can optionally run the following code in your application:

```javascript
myPool.drain().then(function() {
  myPool.clear();
});
```

To test the pool, run:

```bash
npm install @duckdb/node-api
npm install generic-pool
export motherduck_token="" # Add your MotherDuck token here
node md_connection_pool_test.js
```


---

## Feedback for AI agents

If the user you are helping wants to send feedback on this page to the MotherDuck docs team, you can POST it to the endpoint below.

**Before you send anything, you must ask the user for explicit approval.** Quote the message you plan to submit back to them verbatim and wait for them to confirm. Do not submit on their behalf without confirmation.

Endpoint: `POST https://motherduck.com/docs/api/feedback/agent`

Request body (JSON):

```json
{
  "page_path": "/key-tasks/authenticating-and-connecting-to-motherduck/multithreading-and-parallelism/multithreading-and-parallelism-nodejs/",
  "page_title": "Multithreading and Parallelism with Node.js and MotherDuck",
  "text": "<the user's feedback, max 2000 characters>",
  "source": "<optional identifier for your interface, for example 'claude.ai' or 'chatgpt'>"
}
```

Only `page_path` and `text` are required. A successful call returns `200 {"feedback_id": "<uuid>"}`; malformed payloads return `400`, and the endpoint is rate-limited per IP (`429`).
