1- // Import tracing at the top of your entry file
1+ // At the top of your file, before any imports
2+ // Disable tracing during stress tests to improve performance
3+ if ( process . env . STRESS_TEST === 'true' ) {
4+ process . env . DISABLE_TRACING = 'true' ;
5+ process . env . OTEL_LOG_LEVEL = 'error' ;
6+ }
7+
8+ // Import tracing after setting environment variables
29import './tracing' ;
310import express , { Express } from 'express' ;
411import cors from 'cors' ;
@@ -22,13 +29,35 @@ app.use(cors());
2229app . use ( morgan ( 'dev' ) ) ;
2330app . use ( express . json ( ) ) ;
2431
32+ // Add a more robust error handling middleware
33+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
34+ app . use ( ( err : Error , _req : express . Request , res : express . Response , _next : express . NextFunction ) => {
35+ console . error ( 'Error:' , err . message ) ;
36+ res . status ( 500 ) . json ( { error : 'Internal server error' } ) ;
37+ } ) ;
38+
2539// In-memory database
2640const todos : Todo [ ] = [ ] ;
2741let onlineUsers = 0 ;
2842
43+ // Add mutex-like locking mechanism
44+ const locks = new Map < string , boolean > ( ) ;
45+
46+ // Helper function to acquire a lock
47+ const acquireLock = ( id : string ) : boolean => {
48+ if ( locks . has ( id ) ) return false ;
49+ locks . set ( id , true ) ;
50+ return true ;
51+ } ;
52+
53+ // Helper function to release a lock
54+ const releaseLock = ( id : string ) : void => {
55+ locks . delete ( id ) ;
56+ } ;
57+
2958// Socket.IO events
3059io . on ( 'connection' , ( socket ) => {
31- console . log ( 'Client connected:' , socket . id ) ;
60+ console . info ( 'Client connected:' , socket . id ) ;
3261
3362 // Increment online users count and broadcast
3463 onlineUsers ++ ;
@@ -38,22 +67,30 @@ io.on('connection', (socket) => {
3867 socket . emit ( 'todos:init' , todos ) ;
3968
4069 socket . on ( 'disconnect' , ( ) => {
41- console . log ( 'Client disconnected:' , socket . id ) ;
70+ console . info ( 'Client disconnected:' , socket . id ) ;
4271
4372 // Decrement online users count and broadcast
4473 onlineUsers -- ;
4574 io . emit ( 'users:count' , onlineUsers ) ;
4675 } ) ;
4776} ) ;
4877
49- // Helper function to broadcast todo updates
50- const broadcastTodos = ( ) => {
51- io . emit ( 'todos:update' , todos ) ;
52- } ;
53-
54- // Routes
78+ // Add pagination for todos to reduce payload size
5579app . get ( '/api/todos' , ( req , res ) => {
56- res . json ( todos ) ;
80+ const page = parseInt ( req . query . page as string ) || 1 ;
81+ const limit = parseInt ( req . query . limit as string ) || 100 ;
82+ const startIndex = ( page - 1 ) * limit ;
83+ const endIndex = page * limit ;
84+
85+ const paginatedTodos = todos . slice ( startIndex , endIndex ) ;
86+
87+ res . json ( {
88+ todos : paginatedTodos ,
89+ totalCount : todos . length ,
90+ page,
91+ limit,
92+ totalPages : Math . ceil ( todos . length / limit )
93+ } ) ;
5794} ) ;
5895
5996app . post ( '/api/todos' , ( req , res ) => {
@@ -88,43 +125,127 @@ app.get('/api/todos/:id', (req, res) => {
88125 res . json ( todo ) ;
89126} ) ;
90127
91- app . patch ( '/api/todos/:id' , ( req , res ) => {
128+ app . patch ( '/api/todos/:id' , async ( req , res ) => {
92129 const { id } = req . params ;
93130 const updates = req . body as UpdateTodoDto ;
94131
95- const todoIndex = todos . findIndex ( t => t . id === id ) ;
96-
97- if ( todoIndex === - 1 ) {
98- return res . status ( 404 ) . json ( { error : 'Todo not found' } ) ;
132+ // Try to acquire lock
133+ if ( ! acquireLock ( id ) ) {
134+ return res . status ( 409 ) . json ( {
135+ error : 'Resource is currently being modified' ,
136+ retryAfter : 100 // Suggest retry after 100ms
137+ } ) ;
99138 }
100139
101- todos [ todoIndex ] = { ...todos [ todoIndex ] , ...updates } ;
102-
103- // Broadcast the updated todos list
104- broadcastTodos ( ) ;
105-
106- res . json ( todos [ todoIndex ] ) ;
140+ try {
141+ const todoIndex = todos . findIndex ( t => t . id === id ) ;
142+
143+ if ( todoIndex === - 1 ) {
144+ releaseLock ( id ) ;
145+ return res . status ( 404 ) . json ( {
146+ error : 'Todo not found' ,
147+ todoIds : todos . slice ( 0 , 10 ) . map ( t => t . id ) // Send available IDs for debugging
148+ } ) ;
149+ }
150+
151+ todos [ todoIndex ] = { ...todos [ todoIndex ] , ...updates } ;
152+
153+ // Broadcast the updated todos list
154+ broadcastTodos ( ) ;
155+
156+ res . json ( todos [ todoIndex ] ) ;
157+ } finally {
158+ // Always release the lock
159+ releaseLock ( id ) ;
160+ }
107161} ) ;
108162
109- app . delete ( '/api/todos/:id' , ( req , res ) => {
163+ app . delete ( '/api/todos/:id' , async ( req , res ) => {
110164 const { id } = req . params ;
111- const todoIndex = todos . findIndex ( t => t . id === id ) ;
112165
113- if ( todoIndex === - 1 ) {
114- return res . status ( 404 ) . json ( { error : 'Todo not found' } ) ;
166+ // Try to acquire lock
167+ if ( ! acquireLock ( id ) ) {
168+ return res . status ( 409 ) . json ( {
169+ error : 'Resource is currently being modified' ,
170+ retryAfter : 100 // Suggest retry after 100ms
171+ } ) ;
115172 }
116173
117- const deletedTodo = todos [ todoIndex ] ;
118- todos = todos . filter ( t => t . id !== id ) ;
174+ try {
175+ const todoIndex = todos . findIndex ( t => t . id === id ) ;
176+
177+ if ( todoIndex === - 1 ) {
178+ releaseLock ( id ) ;
179+ return res . status ( 404 ) . json ( {
180+ error : 'Todo not found' ,
181+ todoIds : todos . slice ( 0 , 10 ) . map ( t => t . id ) // Send available IDs for debugging
182+ } ) ;
183+ }
184+
185+ const deletedTodo = todos [ todoIndex ] ;
186+
187+ // Use splice to remove the item
188+ todos . splice ( todoIndex , 1 ) ;
189+
190+ // Broadcast the updated todos list
191+ broadcastTodos ( ) ;
192+
193+ res . json ( deletedTodo ) ;
194+ } finally {
195+ // Always release the lock
196+ releaseLock ( id ) ;
197+ }
198+ } ) ;
199+
200+ // Optimize broadcasting by limiting frequency and payload size
201+ let broadcastPending = false ;
202+ const broadcastTodos = ( ) => {
203+ if ( broadcastPending ) return ;
119204
120- // Broadcast the updated todos list
121- broadcastTodos ( ) ;
205+ broadcastPending = true ;
122206
123- res . json ( deletedTodo ) ;
124- } ) ;
207+ // Debounce broadcasts to reduce frequency
208+ setTimeout ( ( ) => {
209+ // Only send the first 100 todos to reduce payload size
210+ const limitedTodos = todos . slice ( 0 , 100 ) ;
211+
212+ // Send the todo IDs separately to help clients track what's available
213+ const todoIds = todos . map ( t => t . id ) ;
214+
215+ io . emit ( 'todos:update' , limitedTodos ) ;
216+ io . emit ( 'todos:ids' , todoIds ) ;
217+ broadcastPending = false ;
218+ } , 100 ) ;
219+ } ;
220+
221+ // Add memory usage monitoring
222+ const logMemoryUsage = ( ) => {
223+ const memoryUsage = process . memoryUsage ( ) ;
224+ console . log ( 'Memory usage:' ) ;
225+ console . log ( ` RSS: ${ Math . round ( memoryUsage . rss / 1024 / 1024 ) } MB` ) ;
226+ console . log ( ` Heap total: ${ Math . round ( memoryUsage . heapTotal / 1024 / 1024 ) } MB` ) ;
227+ console . log ( ` Heap used: ${ Math . round ( memoryUsage . heapUsed / 1024 / 1024 ) } MB` ) ;
228+ } ;
229+
230+ // Log memory usage every 5 seconds during stress test
231+ if ( process . env . STRESS_TEST ) {
232+ setInterval ( logMemoryUsage , 5000 ) ;
233+ }
234+
235+ // Add cleanup to prevent memory leaks
236+ // Periodically clean up old todos if the list gets too large
237+ setInterval ( ( ) => {
238+ if ( todos . length > 1000 ) {
239+ console . log ( `Cleaning up old todos. Before: ${ todos . length } ` ) ;
240+ // Keep only the 500 most recent todos
241+ todos = todos . slice ( - 500 ) ;
242+ console . log ( `After cleanup: ${ todos . length } ` ) ;
243+ broadcastTodos ( ) ;
244+ }
245+ } , 10000 ) ;
125246
126247// Start server
127248httpServer . listen ( PORT , ( ) => {
128- console . log ( `Server running on http://localhost:${ PORT } ` ) ;
129- console . log ( `WebSocket server running on ws://localhost:${ PORT } ` ) ;
249+ console . info ( `Server running on http://localhost:${ PORT } ` ) ;
250+ console . info ( `WebSocket server running on ws://localhost:${ PORT } ` ) ;
130251} ) ;
0 commit comments