mirror of
https://github.com/hcengineering/platform.git
synced 2026-08-24 13:22:23 +02:00
Add client configurable timeouts
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Example: Using custom timeout for BackRPC client
|
||||
*
|
||||
* This example demonstrates how to create a network client with custom timeout values.
|
||||
*/
|
||||
|
||||
import { createNetworkClient } from '@hcengineering/network-client'
|
||||
|
||||
// Example 1: Default behavior - uses environment-based timeout
|
||||
// In development (NODE_ENV=development): 3600 seconds (1 hour)
|
||||
// In production: 3 seconds
|
||||
const defaultClient = createNetworkClient('localhost:3737')
|
||||
|
||||
// Example 2: Custom timeout - 10 minutes
|
||||
const customTimeoutClient = createNetworkClient('localhost:3737', 600)
|
||||
|
||||
// Example 3: Short timeout for fast failure detection - 1 second
|
||||
const fastFailClient = createNetworkClient('localhost:3737', 1)
|
||||
|
||||
// Example 4: Very long timeout for debugging - 2 hours
|
||||
const debugClient = createNetworkClient('localhost:3737', 7200)
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// Wait for connection with optional timeout
|
||||
await defaultClient.waitConnection(5000) // 5 second connection timeout
|
||||
console.log('Connected to server')
|
||||
|
||||
// The client will maintain connection based on its aliveTimeout
|
||||
// Server will detect client as dead only after aliveTimeout seconds of inactivity
|
||||
|
||||
// Your application logic here...
|
||||
|
||||
} catch (error) {
|
||||
console.error('Connection failed:', error)
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error)
|
||||
@@ -525,7 +525,9 @@ describe('backrpc', () => {
|
||||
},
|
||||
'localhost',
|
||||
await server.getPort(),
|
||||
tickMgr
|
||||
tickMgr,
|
||||
undefined,
|
||||
3 // Set timeout to 3 seconds
|
||||
)
|
||||
|
||||
// Wait for initial connection
|
||||
@@ -609,7 +611,9 @@ describe('backrpc', () => {
|
||||
},
|
||||
'localhost',
|
||||
await server.getPort(),
|
||||
tickMgr
|
||||
tickMgr,
|
||||
undefined,
|
||||
3 // Set timeout to 3 seconds
|
||||
)
|
||||
|
||||
// Wait for initial connection
|
||||
@@ -700,7 +704,9 @@ describe('backrpc', () => {
|
||||
},
|
||||
'localhost',
|
||||
await server.getPort(),
|
||||
tickMgr
|
||||
tickMgr,
|
||||
undefined,
|
||||
3 // Set timeout to 3 seconds
|
||||
)
|
||||
|
||||
const client2 = new BackRPCClient(
|
||||
@@ -715,7 +721,9 @@ describe('backrpc', () => {
|
||||
},
|
||||
'localhost',
|
||||
await server.getPort(),
|
||||
tickMgr
|
||||
tickMgr,
|
||||
undefined,
|
||||
3 // Set timeout to 3 seconds
|
||||
)
|
||||
|
||||
// Wait for initial connections
|
||||
|
||||
@@ -50,14 +50,18 @@ export class BackRPCClient<ClientT extends string = ClientId> {
|
||||
|
||||
lastPong: number = 0
|
||||
|
||||
aliveTimeout: number
|
||||
|
||||
constructor (
|
||||
readonly clientId: ClientT,
|
||||
readonly client: BackRPCClientHandler,
|
||||
readonly host: string,
|
||||
readonly port: number,
|
||||
readonly tickMgr: TickManager,
|
||||
options?: zmq.SocketOptions<zmq.Dealer>
|
||||
options?: zmq.SocketOptions<zmq.Dealer>,
|
||||
aliveTimeout?: number
|
||||
) {
|
||||
this.aliveTimeout = aliveTimeout ?? timeouts.aliveTimeout
|
||||
this.dealer = new zmq.Dealer({ ...options, context })
|
||||
|
||||
this.setServerId = () => {}
|
||||
@@ -106,7 +110,7 @@ export class BackRPCClient<ClientT extends string = ClientId> {
|
||||
}
|
||||
|
||||
private async sendHello (): Promise<void> {
|
||||
await this.doSend([backrpcOperations.hello, this.clientId as string, '', ''])
|
||||
await this.doSend([backrpcOperations.hello, this.clientId as string, JSON.stringify({ aliveTimeout: this.aliveTimeout }), ''])
|
||||
}
|
||||
|
||||
private async start (): Promise<void> {
|
||||
|
||||
@@ -39,6 +39,7 @@ interface RPCClientInfo<ClientT extends string> {
|
||||
requestsTotal: number
|
||||
requestsTime: number
|
||||
helloCounter: number
|
||||
aliveTimeout: number // Per-client timeout in seconds
|
||||
}
|
||||
|
||||
export class BackRPCServer<ClientT extends string = ClientId> {
|
||||
@@ -93,9 +94,10 @@ export class BackRPCServer<ClientT extends string = ClientId> {
|
||||
for (const [clientId, clientRecord] of [...this.revClientMapping.entries()]) {
|
||||
const timeSinceLastSeen = now - clientRecord.lastSeen
|
||||
|
||||
if (timeSinceLastSeen > timeouts.aliveTimeout * 1000) {
|
||||
// Use per-client timeout instead of global timeout
|
||||
if (timeSinceLastSeen > clientRecord.aliveTimeout * 1000) {
|
||||
console.warn(
|
||||
`Client ${clientId} has been inactive for ${Math.round(timeSinceLastSeen / 1000)}s, marking as dead`
|
||||
`Client ${clientId} has been inactive for ${Math.round(timeSinceLastSeen / 1000)}s (timeout: ${clientRecord.aliveTimeout}s), marking as dead`
|
||||
)
|
||||
this.handleClose(clientRecord.id, clientId, true)
|
||||
}
|
||||
@@ -177,6 +179,17 @@ export class BackRPCServer<ClientT extends string = ClientId> {
|
||||
const needHello = !this.clientMapping.has(reqId.toString() as ClientT)
|
||||
this.clientMapping.set(reqId.toString() as ClientT, clientId)
|
||||
|
||||
// Parse the timeout from the hello message
|
||||
let clientAliveTimeout = timeouts.aliveTimeout
|
||||
try {
|
||||
const helloData = JSON.parse(payload.toString())
|
||||
if (helloData.aliveTimeout !== undefined) {
|
||||
clientAliveTimeout = helloData.aliveTimeout
|
||||
}
|
||||
} catch (err) {
|
||||
// If parsing fails, use default timeout
|
||||
}
|
||||
|
||||
const clientInfo: RPCClientInfo<ClientT> =
|
||||
this.revClientMapping.get(clientIdText) ??
|
||||
({
|
||||
@@ -185,9 +198,11 @@ export class BackRPCServer<ClientT extends string = ClientId> {
|
||||
requests: new Set(),
|
||||
requestsTime: 0,
|
||||
requestsTotal: 0,
|
||||
helloCounter: 0
|
||||
helloCounter: 0,
|
||||
aliveTimeout: clientAliveTimeout
|
||||
} satisfies RPCClientInfo<ClientT>)
|
||||
clientInfo.helloCounter++
|
||||
clientInfo.aliveTimeout = clientAliveTimeout // Update timeout on reconnection
|
||||
|
||||
this.revClientMapping.set(clientIdText, clientInfo)
|
||||
void this.doSend([clientId, backrpcOperations.hello, this.uuid, ''])
|
||||
|
||||
@@ -98,9 +98,11 @@ export class NetworkClientImpl implements NetworkClient {
|
||||
constructor (
|
||||
readonly host: string,
|
||||
port: number,
|
||||
private readonly tickMgr: TickManager
|
||||
private readonly tickMgr: TickManager,
|
||||
aliveTimeout?: number
|
||||
) {
|
||||
this.client = new BackRPCClient<ClientUuid>(this.clientId, this, host, port, tickMgr)
|
||||
const options = undefined
|
||||
this.client = new BackRPCClient<ClientUuid>(this.clientId, this, host, port, tickMgr, options, aliveTimeout)
|
||||
}
|
||||
|
||||
async waitConnection (timeout?: number): Promise<void> {
|
||||
|
||||
@@ -26,11 +26,11 @@ process.on('exit', () => {
|
||||
shutdownNetworkTickMgr()
|
||||
})
|
||||
|
||||
export function createNetworkClient (url: string): NetworkClient {
|
||||
export function createNetworkClient (url: string, aliveTimeout?: number): NetworkClient {
|
||||
const [host, portStr] = url.split(':')
|
||||
const port = portStr != null ? parseInt(portStr, 10) : 3737
|
||||
tickMgr.start()
|
||||
return new NetworkClientImpl(host, port, tickMgr)
|
||||
return new NetworkClientImpl(host, port, tickMgr, aliveTimeout)
|
||||
}
|
||||
|
||||
export async function createAgent (
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
// Check if we're in Node.js development mode
|
||||
const isDevelopment = typeof process !== 'undefined' && process.env?.NODE_ENV === 'development'
|
||||
|
||||
// In development mode with Node.js, use a huge timeout (1 hour) to allow for debugging
|
||||
const devAliveTimeout = 3600 // 1 hour in seconds
|
||||
|
||||
export const timeouts = {
|
||||
aliveTimeout: 3, // seconds - timeout for detecting dead agents/clients
|
||||
aliveTimeout: isDevelopment ? devAliveTimeout : 3, // seconds - timeout for detecting dead agents/clients
|
||||
unusedContainerTimeout: 5, // 10 seconds for container to be terminated because of being unused
|
||||
pingInterval: 1 // seconds - how often to ping agents
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user