-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
141 lines (129 loc) · 3.96 KB
/
server.js
File metadata and controls
141 lines (129 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/**
* Express Server with ID-based Resource Locking
*
* This server implements a locking mechanism that prevents concurrent processing
* of requests with the same ID. Processing time follows a normal distribution to
* simulate realistic variable workload.
*
* Configuration:
* - PORT: Server listening port (default: 3000)
* - PROCESSING_TIME_MS: Mean processing time in milliseconds (default: 1000)
*
* Endpoints:
* GET /lock?id={id} - Request a lock for the specified ID
*
* @module server
*/
const express = require("express");
const morgan = require("morgan");
const app = express();
const PORT = 3000;
const PROCESSING_TIME_MS = 1000;
/**
* In-memory storage for active locks.
* Key: resource ID, Value: true (lock is held)
* @type {Map<string, boolean>}
*/
const locks = new Map();
// Request logging
app.use(morgan("dev"));
/**
* Generates a random number following a normal (Gaussian) distribution.
* Uses the Box-Muller transform to convert uniform random values to normal distribution.
*
* @param {number} mean - The mean (center) of the distribution
* @param {number} stdDev - The standard deviation (spread) of the distribution
* @returns {number} A random number from the normal distribution
*
* @example
* // Generate processing time with mean 1000ms and stdDev 167ms
* const time = normalRandom(1000, 167);
*/
function normalRandom(mean, stdDev) {
const u1 = Math.random();
const u2 = Math.random();
const z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2);
return z0 * stdDev + mean;
}
/**
* Lock endpoint - Request processing with ID-based locking.
*
* Endpoint: GET /lock?id={id}
*
* Behavior:
* - If the ID is not locked: Acquires lock, processes request (with variable time), returns success
* - If the ID is already locked: Returns 423 (Locked) immediately
* - If ID parameter is missing: Returns 400 (Bad Request)
*
* Processing time follows a normal distribution:
* - Mean: PROCESSING_TIME_MS
* - Standard deviation: PROCESSING_TIME_MS / 6 (~16.7% of mean)
* - ~99.7% of requests complete within ±50% of mean time
*
* @route GET /lock
* @queryparam {string} id - Required. Unique identifier for the resource
* @returns {200} Success - Lock acquired and processing completed
* @returns {400} Bad Request - Missing id parameter
* @returns {423} Locked - Resource is currently locked by another request
*
* @example
* // Successful request
* GET /lock?id=42
* Response: 200 OK
* {
* "success": true,
* "message": "Request for ID 42 processed successfully"
* }
*
* @example
* // Locked resource
* GET /lock?id=42
* Response: 423 Locked
* {
* "error": "Resource is locked",
* "message": "ID 42 is currently locked. Please try again later."
* }
*/
app.get("/lock", async (req, res) => {
const id = req.query.id;
if (!id) {
return res.status(400).json({
error: "Missing parameter",
message: "Please provide an id parameter",
});
}
if (locks.has(id)) {
return res.status(423).json({
error: "Resource is locked",
message: `ID ${id} is currently locked. Please try again later.`,
});
}
// Acquire lock for this ID
locks.set(id, true);
try {
// Generate processing time with normal distribution
// mean = PROCESSING_TIME_MS, standard deviation = PROCESSING_TIME_MS / 6
// This gives ~99.7% of values within ±3σ (within ±50% of mean)
const stdDev = PROCESSING_TIME_MS / 6;
const processingTime = Math.max(
0,
normalRandom(PROCESSING_TIME_MS, stdDev)
);
await new Promise((resolve) => setTimeout(resolve, processingTime));
res.json({
success: true,
message: `Request for ID ${id} processed successfully`,
});
} finally {
// Release lock
locks.delete(id);
}
});
app.listen(PORT, () => {
console.log(
`\x1b[32mServer running on \x1b[36mhttp://localhost:${PORT}\x1b[32m\x1b[0m`
);
console.log(
`\x1b[34mTry the endpoint: \x1b[36mhttp://localhost:${PORT}/lock\x1b[34m\x1b[0m`
);
});