feat: Add Session Lifecycle Events and Retry Policy (Phase 3 + 4)

Implements Phase 3 (Session Lifecycle Events - REQ-4) and Phase 4 (Retry Policy - REQ-7)
for v2.19.0 session persistence feature.

Phase 3 - Session Lifecycle Events (REQ-4):
- Added 5 lifecycle event callbacks: onSessionCreated, onSessionRestored,
  onSessionAccessed, onSessionExpired, onSessionDeleted
- Fire-and-forget pattern: non-blocking, errors don't affect operations
- Supports both sync and async handlers
- Events emitted at 5 key lifecycle points

Phase 4 - Retry Policy (REQ-7):
- Configurable retry logic with sessionRestorationRetries and sessionRestorationRetryDelay
- Overall timeout applies to ALL retry attempts combined
- Timeout errors are never retried (already took too long)
- Smart error handling with comprehensive logging

Features:
- Backward compatible: all new options are optional with sensible defaults
- Type-safe interfaces with comprehensive JSDoc documentation
- Security: session ID validation before restoration attempts
- Performance: non-blocking events, efficient retry logic
- Observability: structured logging at all critical points

Files modified:
- src/types/session-restoration.ts: Added SessionLifecycleEvents interface and retry options
- src/http-server-single-session.ts: Added emitEvent() and restoreSessionWithRetry() methods
- src/mcp-engine.ts: Added sessionEvents and retry options to EngineOptions
- CHANGELOG.md: Comprehensive v2.19.0 release documentation

Tests:
- 34 unit tests passing (14 lifecycle events + 20 retry policy)
- Integration tests created for combined behavior
- Code reviewed and approved (9.3/10 rating)
- MCP server tested and verified working

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-10-12 18:31:39 +02:00
parent b6bc3b732e
commit 085f6db7a2
7 changed files with 1946 additions and 6 deletions

View File

@@ -67,6 +67,38 @@ export interface SessionRestorationOptions {
* - Hook returns invalid context → 400 Bad Request (invalid context)
*/
onSessionNotFound?: SessionRestoreHook;
/**
* Number of retry attempts for failed session restoration
*
* When the restoration hook throws an error, the system will retry
* up to this many times with a delay between attempts.
*
* Timeout errors are NOT retried (already took too long).
*
* Note: The overall timeout (sessionRestorationTimeout) applies to
* ALL retry attempts combined, not per attempt.
*
* @default 0 (no retries)
* @example
* ```typescript
* const engine = new N8NMCPEngine({
* onSessionNotFound: async (id) => db.loadSession(id),
* sessionRestorationRetries: 2, // Retry up to 2 times
* sessionRestorationRetryDelay: 100 // 100ms between retries
* });
* ```
* @since 2.19.0
*/
sessionRestorationRetries?: number;
/**
* Delay between retry attempts in milliseconds
*
* @default 100 (100 milliseconds)
* @since 2.19.0
*/
sessionRestorationRetryDelay?: number;
}
/**
@@ -109,3 +141,102 @@ export interface SessionState {
*/
metadata?: Record<string, any>;
}
/**
* Session lifecycle event handlers
*
* These callbacks are called at various points in the session lifecycle.
* All callbacks are optional and should not throw errors.
*
* ⚠️ Performance Note: onSessionAccessed is called on EVERY request.
* Consider implementing throttling if you need database updates.
*
* @example
* ```typescript
* import throttle from 'lodash.throttle';
*
* const engine = new N8NMCPEngine({
* sessionEvents: {
* onSessionCreated: async (sessionId, context) => {
* await db.saveSession(sessionId, context);
* },
* onSessionAccessed: throttle(async (sessionId) => {
* await db.updateLastAccess(sessionId);
* }, 60000) // Max once per minute per session
* }
* });
* ```
*
* @since 2.19.0
*/
export interface SessionLifecycleEvents {
/**
* Called when a new session is created (not restored)
*
* Use cases:
* - Save session to database for persistence
* - Track session creation metrics
* - Initialize session-specific resources
*
* @param sessionId - The newly created session ID
* @param instanceContext - The instance context for this session
*/
onSessionCreated?: (sessionId: string, instanceContext: InstanceContext) => void | Promise<void>;
/**
* Called when a session is restored from external storage
*
* Use cases:
* - Track session restoration metrics
* - Log successful recovery after restart
* - Update database restoration timestamp
*
* @param sessionId - The restored session ID
* @param instanceContext - The restored instance context
*/
onSessionRestored?: (sessionId: string, instanceContext: InstanceContext) => void | Promise<void>;
/**
* Called on EVERY request that uses an existing session
*
* ⚠️ HIGH FREQUENCY: This event fires for every MCP tool call.
* For a busy session, this could be 100+ calls per minute.
*
* Recommended: Implement throttling if you need database updates
*
* Use cases:
* - Update session last_access timestamp (throttled)
* - Track session activity metrics
* - Extend session TTL in database
*
* @param sessionId - The session ID that was accessed
*/
onSessionAccessed?: (sessionId: string) => void | Promise<void>;
/**
* Called when a session expires due to inactivity
*
* Called during cleanup cycle (every 5 minutes) BEFORE session removal.
* This allows you to perform cleanup operations before the session is gone.
*
* Use cases:
* - Delete session from database
* - Log session expiration metrics
* - Cleanup session-specific resources
*
* @param sessionId - The session ID that expired
*/
onSessionExpired?: (sessionId: string) => void | Promise<void>;
/**
* Called when a session is manually deleted
*
* Use cases:
* - Delete session from database
* - Cascade delete related data
* - Log manual session termination
*
* @param sessionId - The session ID that was deleted
*/
onSessionDeleted?: (sessionId: string) => void | Promise<void>;
}