@joystick.js/db-canary 0.0.0-canary.2251 → 0.0.0-canary.2253
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/database.js +1 -1
- package/dist/client/index.js +1 -1
- package/dist/server/cluster/master.js +4 -4
- package/dist/server/cluster/worker.js +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/lib/auto_index_manager.js +1 -1
- package/dist/server/lib/backup_manager.js +1 -1
- package/dist/server/lib/index_manager.js +1 -1
- package/dist/server/lib/operation_dispatcher.js +1 -1
- package/dist/server/lib/operations/admin.js +1 -1
- package/dist/server/lib/operations/bulk_write.js +1 -1
- package/dist/server/lib/operations/create_index.js +1 -1
- package/dist/server/lib/operations/delete_many.js +1 -1
- package/dist/server/lib/operations/delete_one.js +1 -1
- package/dist/server/lib/operations/find.js +1 -1
- package/dist/server/lib/operations/find_one.js +1 -1
- package/dist/server/lib/operations/insert_one.js +1 -1
- package/dist/server/lib/operations/update_one.js +1 -1
- package/dist/server/lib/send_response.js +1 -1
- package/dist/server/lib/tcp_protocol.js +1 -1
- package/package.json +2 -2
- package/src/client/database.js +159 -133
- package/src/client/index.js +285 -346
- package/src/server/cluster/master.js +265 -156
- package/src/server/cluster/worker.js +26 -18
- package/src/server/index.js +553 -330
- package/src/server/lib/auto_index_manager.js +85 -23
- package/src/server/lib/backup_manager.js +117 -70
- package/src/server/lib/index_manager.js +63 -25
- package/src/server/lib/operation_dispatcher.js +339 -168
- package/src/server/lib/operations/admin.js +343 -205
- package/src/server/lib/operations/bulk_write.js +458 -194
- package/src/server/lib/operations/create_index.js +127 -34
- package/src/server/lib/operations/delete_many.js +204 -67
- package/src/server/lib/operations/delete_one.js +164 -52
- package/src/server/lib/operations/find.js +552 -319
- package/src/server/lib/operations/find_one.js +530 -304
- package/src/server/lib/operations/insert_one.js +147 -52
- package/src/server/lib/operations/update_one.js +334 -93
- package/src/server/lib/send_response.js +37 -17
- package/src/server/lib/tcp_protocol.js +158 -53
- package/test_data_api_key_1758233848259_cglfjzhou/data.mdb +0 -0
- package/test_data_api_key_1758233848259_cglfjzhou/lock.mdb +0 -0
- package/test_data_api_key_1758233848502_urlje2utd/data.mdb +0 -0
- package/test_data_api_key_1758233848502_urlje2utd/lock.mdb +0 -0
- package/test_data_api_key_1758233848738_mtcpfe5ns/data.mdb +0 -0
- package/test_data_api_key_1758233848738_mtcpfe5ns/lock.mdb +0 -0
- package/test_data_api_key_1758233848856_9g97p6gag/data.mdb +0 -0
- package/test_data_api_key_1758233848856_9g97p6gag/lock.mdb +0 -0
- package/test_data_api_key_1758233857008_0tl9zzhj8/data.mdb +0 -0
- package/test_data_api_key_1758233857008_0tl9zzhj8/lock.mdb +0 -0
- package/test_data_api_key_1758233857120_60c2f2uhu/data.mdb +0 -0
- package/test_data_api_key_1758233857120_60c2f2uhu/lock.mdb +0 -0
- package/test_data_api_key_1758233857232_aw7fkqgd9/data.mdb +0 -0
- package/test_data_api_key_1758233857232_aw7fkqgd9/lock.mdb +0 -0
- package/test_data_api_key_1758234881285_4aeflubjb/data.mdb +0 -0
- package/test_data_api_key_1758234881285_4aeflubjb/lock.mdb +0 -0
- package/test_data_api_key_1758234881520_kb0amvtqb/data.mdb +0 -0
- package/test_data_api_key_1758234881520_kb0amvtqb/lock.mdb +0 -0
- package/test_data_api_key_1758234881756_k04gfv2va/data.mdb +0 -0
- package/test_data_api_key_1758234881756_k04gfv2va/lock.mdb +0 -0
- package/test_data_api_key_1758234881876_wn90dpo1z/data.mdb +0 -0
- package/test_data_api_key_1758234881876_wn90dpo1z/lock.mdb +0 -0
- package/test_data_api_key_1758234889461_26xz3dmbr/data.mdb +0 -0
- package/test_data_api_key_1758234889461_26xz3dmbr/lock.mdb +0 -0
- package/test_data_api_key_1758234889572_uziz7e0p5/data.mdb +0 -0
- package/test_data_api_key_1758234889572_uziz7e0p5/lock.mdb +0 -0
- package/test_data_api_key_1758234889684_5f9wmposh/data.mdb +0 -0
- package/test_data_api_key_1758234889684_5f9wmposh/lock.mdb +0 -0
- package/test_data_api_key_1758235657729_prwgm6mxr/data.mdb +0 -0
- package/test_data_api_key_1758235657729_prwgm6mxr/lock.mdb +0 -0
- package/test_data_api_key_1758235657961_rc2da0dc2/data.mdb +0 -0
- package/test_data_api_key_1758235657961_rc2da0dc2/lock.mdb +0 -0
- package/test_data_api_key_1758235658193_oqqxm0sny/data.mdb +0 -0
- package/test_data_api_key_1758235658193_oqqxm0sny/lock.mdb +0 -0
- package/test_data_api_key_1758235658309_vggac1pj6/data.mdb +0 -0
- package/test_data_api_key_1758235658309_vggac1pj6/lock.mdb +0 -0
- package/test_data_api_key_1758235665968_61ko07dd1/data.mdb +0 -0
- package/test_data_api_key_1758235665968_61ko07dd1/lock.mdb +0 -0
- package/test_data_api_key_1758235666082_50lrt6sq8/data.mdb +0 -0
- package/test_data_api_key_1758235666082_50lrt6sq8/lock.mdb +0 -0
- package/test_data_api_key_1758235666194_ykvauwlzh/data.mdb +0 -0
- package/test_data_api_key_1758235666194_ykvauwlzh/lock.mdb +0 -0
- package/test_data_api_key_1758236187207_9c4paeh09/data.mdb +0 -0
- package/test_data_api_key_1758236187207_9c4paeh09/lock.mdb +0 -0
- package/test_data_api_key_1758236187441_4n3o3gkkl/data.mdb +0 -0
- package/test_data_api_key_1758236187441_4n3o3gkkl/lock.mdb +0 -0
- package/test_data_api_key_1758236187672_jt6b21ye0/data.mdb +0 -0
- package/test_data_api_key_1758236187672_jt6b21ye0/lock.mdb +0 -0
- package/test_data_api_key_1758236187788_oo84fz9u6/data.mdb +0 -0
- package/test_data_api_key_1758236187788_oo84fz9u6/lock.mdb +0 -0
- package/test_data_api_key_1758236195507_o9zeznwlm/data.mdb +0 -0
- package/test_data_api_key_1758236195507_o9zeznwlm/lock.mdb +0 -0
- package/test_data_api_key_1758236195619_qsqd60y41/data.mdb +0 -0
- package/test_data_api_key_1758236195619_qsqd60y41/lock.mdb +0 -0
- package/test_data_api_key_1758236195731_im13iq284/data.mdb +0 -0
- package/test_data_api_key_1758236195731_im13iq284/lock.mdb +0 -0
|
@@ -26,6 +26,35 @@ import admin_operation from './operations/admin.js';
|
|
|
26
26
|
|
|
27
27
|
const { create_context_logger } = create_logger('operation_dispatcher');
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Checks if database name exceeds length limit.
|
|
31
|
+
* @param {string} database_name - Database name to check
|
|
32
|
+
* @returns {boolean} True if name exceeds limit
|
|
33
|
+
*/
|
|
34
|
+
const exceeds_length_limit = (database_name) => {
|
|
35
|
+
return database_name.length > 64;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Checks if database name is reserved.
|
|
40
|
+
* @param {string} database_name - Database name to check
|
|
41
|
+
* @returns {boolean} True if name is reserved
|
|
42
|
+
*/
|
|
43
|
+
const is_reserved_database_name = (database_name) => {
|
|
44
|
+
const reserved_names = ['admin', 'config', 'local'];
|
|
45
|
+
return reserved_names.includes(database_name.toLowerCase());
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Checks if database name follows naming convention.
|
|
50
|
+
* @param {string} database_name - Database name to check
|
|
51
|
+
* @returns {boolean} True if name follows convention
|
|
52
|
+
*/
|
|
53
|
+
const follows_naming_convention = (database_name) => {
|
|
54
|
+
const valid_name_pattern = /^[a-zA-Z0-9_-]+$/;
|
|
55
|
+
return valid_name_pattern.test(database_name);
|
|
56
|
+
};
|
|
57
|
+
|
|
29
58
|
/**
|
|
30
59
|
* Validates database name according to naming rules.
|
|
31
60
|
* @param {string} database_name - Database name to validate
|
|
@@ -36,20 +65,268 @@ const validate_database_name = (database_name) => {
|
|
|
36
65
|
return false;
|
|
37
66
|
}
|
|
38
67
|
|
|
39
|
-
|
|
40
|
-
if (database_name.length > 64) {
|
|
68
|
+
if (exceeds_length_limit(database_name)) {
|
|
41
69
|
return false;
|
|
42
70
|
}
|
|
43
71
|
|
|
44
|
-
|
|
45
|
-
const reserved_names = ['admin', 'config', 'local'];
|
|
46
|
-
if (reserved_names.includes(database_name.toLowerCase())) {
|
|
72
|
+
if (is_reserved_database_name(database_name)) {
|
|
47
73
|
return false;
|
|
48
74
|
}
|
|
49
75
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
76
|
+
return follows_naming_convention(database_name);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Creates authentication error response.
|
|
81
|
+
* @returns {Object} Authentication error response
|
|
82
|
+
*/
|
|
83
|
+
const create_authentication_error_response = () => ({
|
|
84
|
+
ok: 0,
|
|
85
|
+
error: 'Authentication required'
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Creates database name validation error response.
|
|
90
|
+
* @returns {Object} Database validation error response
|
|
91
|
+
*/
|
|
92
|
+
const create_database_validation_error_response = () => ({
|
|
93
|
+
ok: 0,
|
|
94
|
+
error: 'Invalid database name. Database names must be alphanumeric with underscores/hyphens, max 64 characters, and cannot be reserved names (admin, config, local).'
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Sends encoded response to socket.
|
|
99
|
+
* @param {net.Socket} socket - Client socket
|
|
100
|
+
* @param {Object} response - Response object
|
|
101
|
+
*/
|
|
102
|
+
const send_encoded_response = (socket, response) => {
|
|
103
|
+
const encoded_response = encode_message(response);
|
|
104
|
+
socket.write(encoded_response);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Logs performance metrics for operation.
|
|
109
|
+
* @param {string} socket_id - Socket identifier
|
|
110
|
+
* @param {string} op_type - Operation type
|
|
111
|
+
* @param {string} collection - Collection name
|
|
112
|
+
* @param {number} duration_ms - Operation duration
|
|
113
|
+
* @param {string} status - Operation status
|
|
114
|
+
* @param {string} error_message - Error message if failed
|
|
115
|
+
* @param {number} request_size - Request size in bytes
|
|
116
|
+
* @param {number} response_size - Response size in bytes
|
|
117
|
+
*/
|
|
118
|
+
const log_operation_performance = (socket_id, op_type, collection, duration_ms, status, error_message, request_size, response_size) => {
|
|
119
|
+
performance_monitor.log_structured_operation(
|
|
120
|
+
socket_id,
|
|
121
|
+
op_type,
|
|
122
|
+
collection,
|
|
123
|
+
duration_ms,
|
|
124
|
+
status,
|
|
125
|
+
error_message,
|
|
126
|
+
request_size,
|
|
127
|
+
response_size
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Logs operation completion details.
|
|
133
|
+
* @param {Function} log - Logger function
|
|
134
|
+
* @param {string} socket_id - Socket identifier
|
|
135
|
+
* @param {string} op_type - Operation type
|
|
136
|
+
* @param {string} collection - Collection name
|
|
137
|
+
* @param {number} duration_ms - Operation duration
|
|
138
|
+
* @param {string} status - Operation status
|
|
139
|
+
* @param {number} request_size - Request size in bytes
|
|
140
|
+
* @param {number} response_size - Response size in bytes
|
|
141
|
+
* @param {string} error_message - Error message if failed
|
|
142
|
+
*/
|
|
143
|
+
const log_operation_details = (log, socket_id, op_type, collection, duration_ms, status, request_size, response_size, error_message = null) => {
|
|
144
|
+
const log_data = {
|
|
145
|
+
client_id: socket_id,
|
|
146
|
+
op: op_type,
|
|
147
|
+
collection,
|
|
148
|
+
duration_ms,
|
|
149
|
+
status,
|
|
150
|
+
request_size
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (status === 'success') {
|
|
154
|
+
log_data.response_size = response_size;
|
|
155
|
+
log.info('Database operation completed', log_data);
|
|
156
|
+
} else {
|
|
157
|
+
log_data.error = error_message;
|
|
158
|
+
log.error('Database operation failed', log_data);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Executes database operation based on type.
|
|
164
|
+
* @param {string} op_type - Operation type
|
|
165
|
+
* @param {string} database_name - Database name
|
|
166
|
+
* @param {Object} data - Operation data
|
|
167
|
+
* @returns {Promise<Object>} Operation result
|
|
168
|
+
*/
|
|
169
|
+
const execute_database_operation = async (op_type, database_name, data) => {
|
|
170
|
+
switch (op_type) {
|
|
171
|
+
case 'insert_one':
|
|
172
|
+
return await insert_one(database_name, data.collection, data.document, data.options);
|
|
173
|
+
|
|
174
|
+
case 'update_one':
|
|
175
|
+
return await update_one(database_name, data.collection, data.filter, data.update, data.options);
|
|
176
|
+
|
|
177
|
+
case 'delete_one':
|
|
178
|
+
return await delete_one(database_name, data.collection, data.filter, data.options);
|
|
179
|
+
|
|
180
|
+
case 'delete_many':
|
|
181
|
+
return await delete_many(database_name, data.collection, data.filter, data.options);
|
|
182
|
+
|
|
183
|
+
case 'bulk_write':
|
|
184
|
+
return await bulk_write(database_name, data.collection, data.operations, data.options);
|
|
185
|
+
|
|
186
|
+
case 'find_one':
|
|
187
|
+
return await find_one(database_name, data.collection, data.filter, data.options);
|
|
188
|
+
|
|
189
|
+
case 'find':
|
|
190
|
+
return await find(database_name, data.collection, data.filter, data.options);
|
|
191
|
+
|
|
192
|
+
case 'create_index':
|
|
193
|
+
return await create_index_operation(database_name, data.collection, data.field, data.options);
|
|
194
|
+
|
|
195
|
+
case 'drop_index':
|
|
196
|
+
return await drop_index_operation(database_name, data.collection, data.field);
|
|
197
|
+
|
|
198
|
+
case 'get_indexes':
|
|
199
|
+
return await get_indexes_operation(database_name, data.collection);
|
|
200
|
+
|
|
201
|
+
default:
|
|
202
|
+
throw new Error(`Unsupported operation: ${op_type}`);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Formats operation result into response object.
|
|
208
|
+
* @param {string} op_type - Operation type
|
|
209
|
+
* @param {Object} result - Operation result
|
|
210
|
+
* @returns {Object} Formatted response
|
|
211
|
+
*/
|
|
212
|
+
const format_operation_response = (op_type, result) => {
|
|
213
|
+
if (op_type === 'find_one') {
|
|
214
|
+
return { ok: 1, document: result };
|
|
215
|
+
} else if (op_type === 'find') {
|
|
216
|
+
return { ok: 1, documents: result };
|
|
217
|
+
} else {
|
|
218
|
+
return { ok: 1, ...result };
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Determines if operation is a write operation.
|
|
224
|
+
* @param {string} op_type - Operation type
|
|
225
|
+
* @returns {boolean} True if write operation
|
|
226
|
+
*/
|
|
227
|
+
const is_write_operation = (op_type) => {
|
|
228
|
+
const read_operations = ['find', 'find_one', 'get_indexes'];
|
|
229
|
+
return !read_operations.includes(op_type);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Queues operation for replication if it's a write operation.
|
|
234
|
+
* @param {string} op_type - Operation type
|
|
235
|
+
* @param {Object} data - Operation data
|
|
236
|
+
*/
|
|
237
|
+
const queue_replication_if_write_operation = (op_type, data) => {
|
|
238
|
+
if (!is_write_operation(op_type)) {
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const replication_manager = get_replication_manager();
|
|
243
|
+
replication_manager.queue_replication(op_type, data.collection, data);
|
|
244
|
+
|
|
245
|
+
setImmediate(() => check_and_grow_map_size());
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Handles successful database operation.
|
|
250
|
+
* @param {net.Socket} socket - Client socket
|
|
251
|
+
* @param {string} op_type - Operation type
|
|
252
|
+
* @param {Object} data - Operation data
|
|
253
|
+
* @param {Object} result - Operation result
|
|
254
|
+
* @param {number} start_time - Operation start time
|
|
255
|
+
* @param {number} raw_data_size - Request size in bytes
|
|
256
|
+
*/
|
|
257
|
+
const handle_successful_operation = (socket, op_type, data, result, start_time, raw_data_size) => {
|
|
258
|
+
const log = create_context_logger();
|
|
259
|
+
const duration_ms = Date.now() - start_time;
|
|
260
|
+
|
|
261
|
+
const response = format_operation_response(op_type, result);
|
|
262
|
+
const encoded_response = encode_message(response);
|
|
263
|
+
const response_size = encoded_response.length;
|
|
264
|
+
|
|
265
|
+
send_encoded_response(socket, response);
|
|
266
|
+
|
|
267
|
+
log_operation_performance(
|
|
268
|
+
socket.id,
|
|
269
|
+
op_type,
|
|
270
|
+
data.collection,
|
|
271
|
+
duration_ms,
|
|
272
|
+
'success',
|
|
273
|
+
null,
|
|
274
|
+
raw_data_size,
|
|
275
|
+
response_size
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
log_operation_details(
|
|
279
|
+
log,
|
|
280
|
+
socket.id,
|
|
281
|
+
op_type,
|
|
282
|
+
data.collection,
|
|
283
|
+
duration_ms,
|
|
284
|
+
'success',
|
|
285
|
+
raw_data_size,
|
|
286
|
+
response_size
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
queue_replication_if_write_operation(op_type, data);
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Handles failed database operation.
|
|
294
|
+
* @param {net.Socket} socket - Client socket
|
|
295
|
+
* @param {string} op_type - Operation type
|
|
296
|
+
* @param {Object} data - Operation data
|
|
297
|
+
* @param {Error} error - Operation error
|
|
298
|
+
* @param {number} start_time - Operation start time
|
|
299
|
+
* @param {number} raw_data_size - Request size in bytes
|
|
300
|
+
*/
|
|
301
|
+
const handle_failed_operation = (socket, op_type, data, error, start_time, raw_data_size) => {
|
|
302
|
+
const log = create_context_logger();
|
|
303
|
+
const duration_ms = Date.now() - start_time;
|
|
304
|
+
|
|
305
|
+
log_operation_performance(
|
|
306
|
+
socket.id,
|
|
307
|
+
op_type,
|
|
308
|
+
data.collection,
|
|
309
|
+
duration_ms,
|
|
310
|
+
'error',
|
|
311
|
+
error.message,
|
|
312
|
+
raw_data_size,
|
|
313
|
+
0
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
log_operation_details(
|
|
317
|
+
log,
|
|
318
|
+
socket.id,
|
|
319
|
+
op_type,
|
|
320
|
+
data.collection,
|
|
321
|
+
duration_ms,
|
|
322
|
+
'error',
|
|
323
|
+
raw_data_size,
|
|
324
|
+
0,
|
|
325
|
+
error.message
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
const error_response = { ok: 0, error: error.message };
|
|
329
|
+
send_encoded_response(socket, error_response);
|
|
53
330
|
};
|
|
54
331
|
|
|
55
332
|
/**
|
|
@@ -58,181 +335,89 @@ const validate_database_name = (database_name) => {
|
|
|
58
335
|
* @param {string} op_type - Type of database operation
|
|
59
336
|
* @param {Object} data - Operation data
|
|
60
337
|
* @param {Function} check_authentication - Function to check if socket is authenticated
|
|
61
|
-
* @param {number}
|
|
62
|
-
* @param {Object}
|
|
63
|
-
* @param {Set}
|
|
338
|
+
* @param {number} raw_data_size - Size of raw request data for monitoring
|
|
339
|
+
* @param {Object} connection_manager - Connection manager instance (optional)
|
|
340
|
+
* @param {Set} authenticated_clients - Set of authenticated client IDs (optional)
|
|
64
341
|
*/
|
|
65
342
|
export const handle_database_operation = async (socket, op_type, data, check_authentication, raw_data_size = 0, connection_manager = null, authenticated_clients = null) => {
|
|
66
|
-
const log = create_context_logger();
|
|
67
343
|
const start_time = Date.now();
|
|
68
344
|
|
|
69
345
|
if (!check_authentication(socket)) {
|
|
70
|
-
const response =
|
|
71
|
-
|
|
72
|
-
socket.
|
|
73
|
-
performance_monitor.log_structured_operation(socket.id, op_type, null, 0, 'error', 'Authentication required', raw_data_size, 0);
|
|
346
|
+
const response = create_authentication_error_response();
|
|
347
|
+
send_encoded_response(socket, response);
|
|
348
|
+
log_operation_performance(socket.id, op_type, null, 0, 'error', 'Authentication required', raw_data_size, 0);
|
|
74
349
|
return;
|
|
75
350
|
}
|
|
76
351
|
|
|
77
|
-
// NOTE: Extract and validate database name from request.
|
|
78
352
|
const database_name = data.database || 'default';
|
|
79
353
|
|
|
80
354
|
if (!validate_database_name(database_name)) {
|
|
81
|
-
const response =
|
|
82
|
-
|
|
83
|
-
socket.
|
|
84
|
-
performance_monitor.log_structured_operation(socket.id, op_type, data.collection, 0, 'error', 'Invalid database name', raw_data_size, 0);
|
|
355
|
+
const response = create_database_validation_error_response();
|
|
356
|
+
send_encoded_response(socket, response);
|
|
357
|
+
log_operation_performance(socket.id, op_type, data.collection, 0, 'error', 'Invalid database name', raw_data_size, 0);
|
|
85
358
|
return;
|
|
86
359
|
}
|
|
87
360
|
|
|
88
|
-
// NOTE: Check if this is a secondary node and should forward write operations.
|
|
89
361
|
const write_forwarder = get_write_forwarder();
|
|
90
362
|
const forwarded = await write_forwarder.forward_operation(socket, op_type, data);
|
|
91
363
|
|
|
92
364
|
if (forwarded) {
|
|
93
|
-
// Operation was forwarded to primary, response will be handled by forwarder
|
|
94
365
|
return;
|
|
95
366
|
}
|
|
96
367
|
|
|
97
368
|
try {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
switch (op_type) {
|
|
101
|
-
case 'insert_one':
|
|
102
|
-
result = await insert_one(database_name, data.collection, data.document, data.options);
|
|
103
|
-
break;
|
|
104
|
-
|
|
105
|
-
case 'update_one':
|
|
106
|
-
result = await update_one(database_name, data.collection, data.filter, data.update, data.options);
|
|
107
|
-
break;
|
|
108
|
-
|
|
109
|
-
case 'delete_one':
|
|
110
|
-
result = await delete_one(database_name, data.collection, data.filter, data.options);
|
|
111
|
-
break;
|
|
112
|
-
|
|
113
|
-
case 'delete_many':
|
|
114
|
-
result = await delete_many(database_name, data.collection, data.filter, data.options);
|
|
115
|
-
break;
|
|
116
|
-
|
|
117
|
-
case 'bulk_write':
|
|
118
|
-
result = await bulk_write(database_name, data.collection, data.operations, data.options);
|
|
119
|
-
break;
|
|
120
|
-
|
|
121
|
-
case 'find_one':
|
|
122
|
-
result = await find_one(database_name, data.collection, data.filter, data.options);
|
|
123
|
-
break;
|
|
124
|
-
|
|
125
|
-
case 'find':
|
|
126
|
-
result = await find(database_name, data.collection, data.filter, data.options);
|
|
127
|
-
break;
|
|
128
|
-
|
|
129
|
-
case 'create_index':
|
|
130
|
-
result = await create_index_operation(database_name, data.collection, data.field, data.options);
|
|
131
|
-
break;
|
|
132
|
-
|
|
133
|
-
case 'drop_index':
|
|
134
|
-
result = await drop_index_operation(database_name, data.collection, data.field);
|
|
135
|
-
break;
|
|
136
|
-
|
|
137
|
-
case 'get_indexes':
|
|
138
|
-
result = await get_indexes_operation(database_name, data.collection);
|
|
139
|
-
break;
|
|
140
|
-
|
|
141
|
-
default:
|
|
142
|
-
throw new Error(`Unsupported operation: ${op_type}`);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const duration_ms = Date.now() - start_time;
|
|
146
|
-
|
|
147
|
-
// NOTE: All operations now return structured responses for consistency.
|
|
148
|
-
let response;
|
|
149
|
-
if (op_type === 'find_one') {
|
|
150
|
-
response = { ok: 1, document: result };
|
|
151
|
-
} else if (op_type === 'find') {
|
|
152
|
-
response = { ok: 1, documents: result };
|
|
153
|
-
} else {
|
|
154
|
-
response = { ok: 1, ...result };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const encoded_response = encode_message(response);
|
|
158
|
-
const response_size = encoded_response.length;
|
|
159
|
-
|
|
160
|
-
socket.write(encoded_response);
|
|
161
|
-
|
|
162
|
-
// NOTE: Log structured operation with performance monitoring.
|
|
163
|
-
performance_monitor.log_structured_operation(
|
|
164
|
-
socket.id,
|
|
165
|
-
op_type,
|
|
166
|
-
data.collection,
|
|
167
|
-
duration_ms,
|
|
168
|
-
'success',
|
|
169
|
-
null,
|
|
170
|
-
raw_data_size,
|
|
171
|
-
response_size
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
log.info('Database operation completed', {
|
|
175
|
-
client_id: socket.id,
|
|
176
|
-
op: op_type,
|
|
177
|
-
collection: data.collection,
|
|
178
|
-
duration_ms,
|
|
179
|
-
status: 'success',
|
|
180
|
-
request_size: raw_data_size,
|
|
181
|
-
response_size
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// NOTE: Queue write operations for replication to secondary nodes.
|
|
185
|
-
if (op_type !== 'find' && op_type !== 'find_one' && op_type !== 'get_indexes') {
|
|
186
|
-
const replication_manager = get_replication_manager();
|
|
187
|
-
replication_manager.queue_replication(op_type, data.collection, data);
|
|
188
|
-
|
|
189
|
-
setImmediate(() => check_and_grow_map_size());
|
|
190
|
-
}
|
|
369
|
+
const result = await execute_database_operation(op_type, database_name, data);
|
|
370
|
+
handle_successful_operation(socket, op_type, data, result, start_time, raw_data_size);
|
|
191
371
|
} catch (error) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const error_response = { ok: 0, error: error.message };
|
|
217
|
-
const encoded_error_response = encode_message(error_response);
|
|
218
|
-
socket.write(encoded_error_response);
|
|
372
|
+
handle_failed_operation(socket, op_type, data, error, start_time, raw_data_size);
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Creates admin authentication error response.
|
|
378
|
+
* @returns {Object} Admin authentication error response
|
|
379
|
+
*/
|
|
380
|
+
const create_admin_authentication_error_response = () => ({
|
|
381
|
+
ok: false,
|
|
382
|
+
error: 'Authentication required'
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Creates admin operation response.
|
|
387
|
+
* @param {string} admin_action - Admin action type
|
|
388
|
+
* @param {Object} result - Operation result
|
|
389
|
+
* @returns {Object} Admin response object
|
|
390
|
+
*/
|
|
391
|
+
const create_admin_operation_response = (admin_action, result) => {
|
|
392
|
+
if (!admin_action) {
|
|
393
|
+
return { ok: true, ...result };
|
|
394
|
+
} else {
|
|
395
|
+
return { ok: 1, ...result };
|
|
219
396
|
}
|
|
220
397
|
};
|
|
221
398
|
|
|
399
|
+
/**
|
|
400
|
+
* Creates admin operation error response.
|
|
401
|
+
* @param {string} error_message - Error message
|
|
402
|
+
* @returns {Object} Admin error response
|
|
403
|
+
*/
|
|
404
|
+
const create_admin_operation_error_response = (error_message) => ({
|
|
405
|
+
ok: 0,
|
|
406
|
+
error: `Admin operation failed: ${error_message}`
|
|
407
|
+
});
|
|
408
|
+
|
|
222
409
|
/**
|
|
223
410
|
* Handles admin operations with authentication and error handling.
|
|
224
411
|
* @param {net.Socket} socket - Client socket connection
|
|
225
412
|
* @param {Object} data - Admin operation data
|
|
226
413
|
* @param {Function} check_authentication - Function to check if socket is authenticated
|
|
227
|
-
* @param {Object}
|
|
228
|
-
* @param {Set}
|
|
414
|
+
* @param {Object} connection_manager - Connection manager instance (optional)
|
|
415
|
+
* @param {Set} authenticated_clients - Set of authenticated client IDs (optional)
|
|
229
416
|
*/
|
|
230
417
|
export const handle_admin_operation = async (socket, data, check_authentication, connection_manager = null, authenticated_clients = null) => {
|
|
231
418
|
if (!check_authentication(socket)) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const encoded_response = encode_message(response);
|
|
235
|
-
socket.write(encoded_response);
|
|
419
|
+
const response = create_admin_authentication_error_response();
|
|
420
|
+
send_encoded_response(socket, response);
|
|
236
421
|
return;
|
|
237
422
|
}
|
|
238
423
|
|
|
@@ -241,24 +426,11 @@ export const handle_admin_operation = async (socket, data, check_authentication,
|
|
|
241
426
|
const admin_data = data || {};
|
|
242
427
|
|
|
243
428
|
const result = await admin_operation(admin_action, admin_data, connection_manager, authenticated_clients);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if (!admin_action) {
|
|
247
|
-
// NOTE: Default admin operation (backward compatibility) - return result directly with ok: true.
|
|
248
|
-
const response = { ok: true, ...result };
|
|
249
|
-
const encoded_response = encode_message(response);
|
|
250
|
-
socket.write(encoded_response);
|
|
251
|
-
} else {
|
|
252
|
-
// NOTE: Specific admin actions - return result directly with ok: 1.
|
|
253
|
-
const response = { ok: 1, ...result };
|
|
254
|
-
const encoded_response = encode_message(response);
|
|
255
|
-
socket.write(encoded_response);
|
|
256
|
-
}
|
|
429
|
+
const response = create_admin_operation_response(admin_action, result);
|
|
430
|
+
send_encoded_response(socket, response);
|
|
257
431
|
} catch (error) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const encoded_response = encode_message(response);
|
|
261
|
-
socket.write(encoded_response);
|
|
432
|
+
const response = create_admin_operation_error_response(error.message);
|
|
433
|
+
send_encoded_response(socket, response);
|
|
262
434
|
}
|
|
263
435
|
};
|
|
264
436
|
|
|
@@ -268,6 +440,5 @@ export const handle_admin_operation = async (socket, data, check_authentication,
|
|
|
268
440
|
*/
|
|
269
441
|
export const handle_ping_operation = (socket) => {
|
|
270
442
|
const ping_response = { ok: 1 };
|
|
271
|
-
|
|
272
|
-
socket.write(encoded_ping_response);
|
|
443
|
+
send_encoded_response(socket, ping_response);
|
|
273
444
|
};
|