Overview
TheResourcePool class manages a pool of reusable resources (database connections, HTTP clients, worker threads, etc.) with automatic lifecycle management, health checks, and cleanup.
Resource pools are useful for managing expensive-to-create resources that can be reused across multiple operations.
Creation
Resource pools are created usingscope.pool():
import { scope } from 'go-go-scope'
await using s = scope()
const pool = s.pool({
create: async () => {
const conn = await createDatabaseConnection()
return conn
},
destroy: async (conn) => {
await conn.close()
},
min: 2,
max: 10,
acquireTimeout: 5000,
healthCheck: async (conn) => {
try {
await conn.query('SELECT 1')
return { healthy: true }
} catch (err) {
return { healthy: false, message: err.message }
}
},
healthCheckInterval: 30000 // Check every 30s
})
Resource pool configuration
Show properties
Show properties
Factory function to create a new resource
Cleanup function to destroy a resource
Minimum number of resources to maintain. Default: 0
Maximum number of resources in the pool
Maximum time to wait for resource acquisition in milliseconds. Default: 30000
Optional health check function to validate resources
Interval between periodic health checks in milliseconds. Only used if healthCheck is provided. Default: 30000
A new ResourcePool instance
Core Methods
acquire()
Acquire a resource from the pool. Blocks if no resources are available.const pool = s.pool({
create: () => createConnection(),
destroy: (conn) => conn.close(),
max: 10
})
const conn = await pool.acquire()
try {
const result = await conn.query('SELECT * FROM users')
console.log(result)
} finally {
// Must release!
await pool.release(conn)
}
Acquired resource
Always release acquired resources using
release(). Consider using execute() for automatic cleanup.release()
Release a resource back to the pool.const conn = await pool.acquire()
try {
await conn.query('SELECT 1')
} finally {
await pool.release(conn)
}
Resource to release
execute()
Acquire a resource, execute a function, and automatically release.const pool = s.pool({
create: () => createConnection(),
destroy: (conn) => conn.close(),
max: 10
})
const result = await pool.execute(async (conn) => {
return await conn.query('SELECT * FROM users')
})
// Resource automatically released
Function to execute with acquired resource
Function result
initialize()
Manually initialize the pool with minimum resources.const pool = s.pool({
create: () => createConnection(),
destroy: (conn) => conn.close(),
min: 5,
max: 20
})
// Pre-create minimum resources
await pool.initialize()
console.log(pool.stats.total) // 5
The pool automatically initializes on first
acquire() if not already initialized.checkHealth()
Manually trigger a health check on all resources.const pool = s.pool({
create: () => createConnection(),
destroy: (conn) => conn.close(),
max: 10,
healthCheck: async (conn) => {
try {
await conn.ping()
return { healthy: true }
} catch {
return { healthy: false }
}
}
})
// Manually check health
const unhealthyCount = await pool.checkHealth()
console.log(`Removed ${unhealthyCount} unhealthy resources`)
Number of unhealthy resources found and removed
Properties
stats
Pool statistics.const pool = s.pool({
create: () => createConnection(),
destroy: (conn) => conn.close(),
min: 2,
max: 10
})
await pool.initialize()
const stats = pool.stats
console.log(stats)
// {
// total: 2,
// available: 2,
// inUse: 0,
// waiting: 0,
// creating: 0,
// unhealthy: 0,
// healthChecksEnabled: false
// }
Show properties
Show properties
Total number of resources in pool
Number of available resources
Number of resources currently in use
Number of tasks waiting to acquire a resource
Number of resources currently being created
Number of resources that failed health checks
Whether health checks are enabled
Health Checks
Periodic Health Checks
Automatically validate resources at regular intervals:const pool = s.pool({
create: () => createConnection(),
destroy: (conn) => conn.close(),
max: 10,
healthCheck: async (conn) => {
try {
await conn.query('SELECT 1')
return { healthy: true }
} catch (err) {
return {
healthy: false,
message: `Health check failed: ${err.message}`
}
}
},
healthCheckInterval: 30000 // Check every 30 seconds
})
// Unhealthy resources are automatically removed and replaced
Manual Health Checks
const pool = s.pool({
create: () => createConnection(),
destroy: (conn) => conn.close(),
max: 10,
healthCheck: async (conn) => {
const isConnected = await conn.isConnected()
return { healthy: isConnected }
}
})
// Trigger health check manually
const removed = await pool.checkHealth()
console.log(`Removed ${removed} unhealthy connections`)
// Pool automatically replenishes to maintain min size
Patterns
Database Connection Pool
import { scope } from 'go-go-scope'
import { createConnection } from 'mysql2/promise'
await using s = scope()
const pool = s.pool({
create: async () => {
const conn = await createConnection({
host: 'localhost',
user: 'root',
database: 'mydb'
})
return conn
},
destroy: async (conn) => {
await conn.end()
},
min: 5,
max: 20,
acquireTimeout: 5000,
healthCheck: async (conn) => {
try {
await conn.ping()
return { healthy: true }
} catch (err) {
return { healthy: false, message: err.message }
}
},
healthCheckInterval: 30000
})
// Use the pool
const users = await pool.execute(async (conn) => {
const [rows] = await conn.query('SELECT * FROM users')
return rows
})
HTTP Client Pool
import { scope } from 'go-go-scope'
import axios from 'axios'
await using s = scope()
const pool = s.pool({
create: async () => {
return axios.create({
baseURL: 'https://api.example.com',
timeout: 5000,
headers: { 'User-Agent': 'MyApp/1.0' }
})
},
destroy: async (client) => {
// Cleanup if needed
},
min: 2,
max: 10,
healthCheck: async (client) => {
try {
await client.get('/health')
return { healthy: true }
} catch {
return { healthy: false }
}
},
healthCheckInterval: 60000
})
const data = await pool.execute(async (client) => {
const response = await client.get('/api/data')
return response.data
})
Worker Thread Pool
import { scope } from 'go-go-scope'
import { Worker } from 'worker_threads'
await using s = scope()
const pool = s.pool({
create: async () => {
return new Worker('./worker.js')
},
destroy: async (worker) => {
await worker.terminate()
},
min: 4,
max: 16,
healthCheck: async (worker) => {
return new Promise((resolve) => {
const timeout = setTimeout(() => {
resolve({ healthy: false, message: 'Health check timeout' })
}, 1000)
worker.once('message', (msg) => {
clearTimeout(timeout)
if (msg === 'pong') {
resolve({ healthy: true })
} else {
resolve({ healthy: false })
}
})
worker.postMessage('ping')
})
},
healthCheckInterval: 30000
})
const result = await pool.execute(async (worker) => {
return new Promise((resolve, reject) => {
worker.once('message', resolve)
worker.once('error', reject)
worker.postMessage({ task: 'compute', data: [1, 2, 3] })
})
})
Redis Connection Pool
import { scope } from 'go-go-scope'
import { createClient } from 'redis'
await using s = scope()
const pool = s.pool({
create: async () => {
const client = createClient({
url: 'redis://localhost:6379'
})
await client.connect()
return client
},
destroy: async (client) => {
await client.disconnect()
},
min: 3,
max: 15,
acquireTimeout: 3000,
healthCheck: async (client) => {
try {
await client.ping()
return { healthy: true }
} catch (err) {
return { healthy: false, message: err.message }
}
},
healthCheckInterval: 20000
})
// Get value
const value = await pool.execute(async (client) => {
return await client.get('mykey')
})
// Set value
await pool.execute(async (client) => {
await client.set('mykey', 'myvalue')
})
Resource Pool with Metrics
import { scope } from 'go-go-scope'
await using s = scope()
const pool = s.pool({
create: async () => {
const start = Date.now()
const conn = await createConnection()
metrics.histogram('pool.create.duration', Date.now() - start)
return conn
},
destroy: async (conn) => {
const start = Date.now()
await conn.close()
metrics.histogram('pool.destroy.duration', Date.now() - start)
},
min: 5,
max: 20,
healthCheck: async (conn) => {
try {
await conn.ping()
metrics.increment('pool.health_check.success')
return { healthy: true }
} catch (err) {
metrics.increment('pool.health_check.failure')
return { healthy: false, message: err.message }
}
},
healthCheckInterval: 30000
})
// Track pool metrics
setInterval(() => {
const stats = pool.stats
metrics.gauge('pool.total', stats.total)
metrics.gauge('pool.available', stats.available)
metrics.gauge('pool.in_use', stats.inUse)
metrics.gauge('pool.waiting', stats.waiting)
metrics.gauge('pool.unhealthy', stats.unhealthy)
}, 10000)
Examples
Basic Usage
import { scope } from 'go-go-scope'
interface Connection {
query: (sql: string) => Promise<unknown[]>
close: () => Promise<void>
}
await using s = scope()
const pool = s.pool<Connection>({
create: async () => {
console.log('Creating connection')
return await createConnection()
},
destroy: async (conn) => {
console.log('Destroying connection')
await conn.close()
},
min: 2,
max: 5
})
// Execute multiple queries
const results = await Promise.all([
pool.execute((conn) => conn.query('SELECT * FROM users')),
pool.execute((conn) => conn.query('SELECT * FROM orders')),
pool.execute((conn) => conn.query('SELECT * FROM products'))
])
console.log(pool.stats)
// { total: 3, available: 3, inUse: 0, ... }
With Health Checks
import { scope } from 'go-go-scope'
await using s = scope()
const pool = s.pool({
create: () => createConnection(),
destroy: (conn) => conn.close(),
min: 3,
max: 10,
healthCheck: async (conn) => {
try {
const result = await conn.query('SELECT 1')
return { healthy: true }
} catch (err) {
console.error('Health check failed:', err)
return {
healthy: false,
message: `Connection unhealthy: ${err.message}`
}
}
},
healthCheckInterval: 30000
})
// Unhealthy connections are automatically removed and replaced
Manual Acquire/Release
import { scope } from 'go-go-scope'
await using s = scope()
const pool = s.pool({
create: () => createConnection(),
destroy: (conn) => conn.close(),
max: 10,
acquireTimeout: 5000
})
// Manual acquire/release
const conn = await pool.acquire()
try {
await conn.query('BEGIN')
await conn.query('INSERT INTO users VALUES (1, "Alice")')
await conn.query('INSERT INTO orders VALUES (1, 1, 100)')
await conn.query('COMMIT')
} catch (err) {
await conn.query('ROLLBACK')
throw err
} finally {
await pool.release(conn)
}
Timeout Handling
import { scope } from 'go-go-scope'
await using s = scope()
const pool = s.pool({
create: () => createConnection(),
destroy: (conn) => conn.close(),
max: 2,
acquireTimeout: 3000 // 3 second timeout
})
// Hold both resources
const conn1 = await pool.acquire()
const conn2 = await pool.acquire()
// This will timeout after 3 seconds
try {
const conn3 = await pool.acquire()
} catch (err) {
console.error('Timeout:', err.message)
// "Acquire timeout after 3000ms"
}
await pool.release(conn1)
await pool.release(conn2)