@arela/uploader 1.0.23 → 1.1.0
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/docs/AUTO_PROCESSING_PIPELINE.md +258 -0
- package/docs/COMPLETE_USAGE_GUIDE.md +1363 -0
- package/docs/DATABASESERVICE_IMPROVEMENTS.md +546 -0
- package/docs/PASO_2_TEST_RESULTS.md +298 -0
- package/docs/PASO_3_PLAN.md +385 -0
- package/docs/PHASE_1_FILE_DETECTION.md +366 -0
- package/docs/PHASE_2_API_INTEGRATION.md +426 -0
- package/docs/PHASE_3_DATABASE_MANAGEMENT.md +480 -0
- package/docs/PHASE_4_FILE_OPERATIONS.md +448 -0
- package/docs/PHASE_5_WATCH_MODE.md +450 -0
- package/docs/PHASE_6_SIGNAL_HANDLING.md +472 -0
- package/docs/PHASE_7_ADVANCED_FEATURES.md +560 -0
- package/docs/PLAN_WATCH_FEATURE.md +417 -0
- package/docs/README.md +480 -0
- package/docs/SCHEMA_ALIGNMENT_SUMMARY.md +301 -0
- package/docs/SMARTWATCH_DATABASE_REFACTORING.md +181 -0
- package/docs/SMART_WATCH_DATABASE_CHANGES.md +502 -0
- package/docs/TESTING_WATCH_MODE.md +212 -0
- package/docs/WATCHER_API_IMPLEMENTATION.md +520 -0
- package/docs/WATCHER_API_INTEGRATION.md +562 -0
- package/docs/WATCHER_SETUP_GUIDE.md +614 -0
- package/docs/WATCH_ARCHITECTURE.md +395 -0
- package/docs/WATCH_AUTO_PIPELINE.md +334 -0
- package/docs/WATCH_CONFIGURATION.md +267 -0
- package/docs/WATCH_USAGE_GUIDE.md +567 -0
- package/docs/commands.md +14 -0
- package/package.json +1 -1
- package/scripts/scoring-compare.js +243 -0
- package/scripts/scoring-phase4-check.js +96 -0
- package/src/commands/IdentifyCommand.js +36 -0
- package/src/config/config.js +2 -2
- package/src/file-detection.js +71 -4
- package/src/scoring/db-matcher-adapter.js +98 -0
- package/src/scoring/matchers-seed.js +386 -0
- package/src/scoring/scoring-engine.js +246 -0
- package/src/services/ScanApiService.js +14 -0
- package/tests/unit/scoring-engine.test.js +221 -0
- package/.vscode/settings.json +0 -1
- package/coverage/IdentifyCommand.js.html +0 -1462
- package/coverage/PropagateCommand.js.html +0 -1507
- package/coverage/PushCommand.js.html +0 -1504
- package/coverage/ScanCommand.js.html +0 -1654
- package/coverage/UploadCommand.js.html +0 -1846
- package/coverage/WatchCommand.js.html +0 -4111
- package/coverage/base.css +0 -224
- package/coverage/block-navigation.js +0 -87
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +0 -191
- package/coverage/lcov-report/IdentifyCommand.js.html +0 -1462
- package/coverage/lcov-report/PropagateCommand.js.html +0 -1507
- package/coverage/lcov-report/PushCommand.js.html +0 -1504
- package/coverage/lcov-report/ScanCommand.js.html +0 -1654
- package/coverage/lcov-report/UploadCommand.js.html +0 -1846
- package/coverage/lcov-report/WatchCommand.js.html +0 -4111
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -191
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov.info +0 -1937
- package/coverage/prettify.css +0 -1
- package/coverage/prettify.js +0 -2
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +0 -210
- package/docs/API_ENDPOINTS_FOR_DETECTION.md +0 -647
- package/docs/API_RETRY_MECHANISM.md +0 -338
- package/docs/ARELA_IDENTIFY_IMPLEMENTATION.md +0 -489
- package/docs/ARELA_IDENTIFY_QUICKREF.md +0 -186
- package/docs/ARELA_PROPAGATE_IMPLEMENTATION.md +0 -581
- package/docs/ARELA_PROPAGATE_QUICKREF.md +0 -272
- package/docs/ARELA_PUSH_IMPLEMENTATION.md +0 -577
- package/docs/ARELA_PUSH_QUICKREF.md +0 -322
- package/docs/ARELA_SCAN_IMPLEMENTATION.md +0 -373
- package/docs/ARELA_SCAN_QUICKREF.md +0 -139
- package/docs/CROSS_PLATFORM_PATH_HANDLING.md +0 -597
- package/docs/DETECTION_ATTEMPT_TRACKING.md +0 -414
- package/docs/MIGRATION_UPLOADER_TO_FILE_STATS.md +0 -1020
- package/docs/MULTI_LEVEL_DIRECTORY_SCANNING.md +0 -494
- package/docs/QUICK_REFERENCE_API_DETECTION.md +0 -264
- package/docs/REFACTORING_SUMMARY_DETECT_PEDIMENTOS.md +0 -200
- package/docs/STATS_COMMAND_SEQUENCE_DIAGRAM.md +0 -287
- package/docs/STATS_COMMAND_SIMPLE.md +0 -93
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
# DatabaseService - Mejoras Fase 5
|
|
2
|
+
|
|
3
|
+
**Archivo**: `src/services/DatabaseService.js`
|
|
4
|
+
**Tamaño Original**: 1692 líneas
|
|
5
|
+
**Tamaño Nuevo**: 2078 líneas
|
|
6
|
+
**Líneas Agregadas**: +386
|
|
7
|
+
**Métodos Originales**: 16
|
|
8
|
+
**Métodos Nuevos**: 6
|
|
9
|
+
**Total Métodos**: 22
|
|
10
|
+
**Validación**: ✅ Sintaxis correcta
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 📋 Resumen de Cambios
|
|
15
|
+
|
|
16
|
+
Se agregaron 6 nuevos métodos al DatabaseService para soportar el tracking de eventos de upload, reintentos y generación de estadísticas. Estos métodos se integran directamente con el LoggingService y habilitan la persistencia de datos en las tablas `watch_uploads` y `watch_events`.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 🆕 Nuevos Métodos Implementados
|
|
21
|
+
|
|
22
|
+
### 1. insertUploadEvent(uploadEvent, sessionId)
|
|
23
|
+
**Propósito**: Guardar evento de upload en la tabla `watch_uploads`
|
|
24
|
+
|
|
25
|
+
**Parámetros**:
|
|
26
|
+
- `uploadEvent` (Object): Evento de upload del LoggingService
|
|
27
|
+
- timestamp (ISO8601)
|
|
28
|
+
- strategy ('individual' | 'batch' | 'full-structure')
|
|
29
|
+
- fileCount (number)
|
|
30
|
+
- successCount (number)
|
|
31
|
+
- failureCount (number)
|
|
32
|
+
- retryCount (number)
|
|
33
|
+
- duration (milliseconds)
|
|
34
|
+
- status (string)
|
|
35
|
+
- metadata (object)
|
|
36
|
+
- `sessionId` (string): ID único de sesión
|
|
37
|
+
|
|
38
|
+
**Retorna**: Promise<Object> - Registro insertado
|
|
39
|
+
|
|
40
|
+
**Ejemplo**:
|
|
41
|
+
```javascript
|
|
42
|
+
const uploadEvent = await databaseService.insertUploadEvent({
|
|
43
|
+
timestamp: '2025-11-14T10:30:00Z',
|
|
44
|
+
strategy: 'batch',
|
|
45
|
+
fileCount: 20,
|
|
46
|
+
successCount: 18,
|
|
47
|
+
failureCount: 2,
|
|
48
|
+
retryCount: 3,
|
|
49
|
+
duration: 5000,
|
|
50
|
+
status: 'completed',
|
|
51
|
+
metadata: { notes: 'Batch upload completed' }
|
|
52
|
+
}, 'session-123');
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Tabla watch_uploads**:
|
|
56
|
+
```sql
|
|
57
|
+
{
|
|
58
|
+
id: UUID (auto-generated),
|
|
59
|
+
session_id: VARCHAR,
|
|
60
|
+
timestamp: TIMESTAMP,
|
|
61
|
+
strategy: VARCHAR ('individual' | 'batch' | 'full-structure'),
|
|
62
|
+
file_count: INTEGER,
|
|
63
|
+
success_count: INTEGER,
|
|
64
|
+
failure_count: INTEGER,
|
|
65
|
+
retry_count: INTEGER,
|
|
66
|
+
duration_ms: INTEGER,
|
|
67
|
+
status: VARCHAR,
|
|
68
|
+
metadata: JSONB,
|
|
69
|
+
created_at: TIMESTAMP (auto)
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### 2. insertRetryEvent(uploadEventId, sessionId, retryEvent)
|
|
76
|
+
**Propósito**: Guardar evento de reintento en la tabla `watch_events`
|
|
77
|
+
|
|
78
|
+
**Parámetros**:
|
|
79
|
+
- `uploadEventId` (string): ID del evento de upload padre
|
|
80
|
+
- `sessionId` (string): ID de sesión
|
|
81
|
+
- `retryEvent` (Object): Evento de reintento del LoggingService
|
|
82
|
+
- timestamp (ISO8601)
|
|
83
|
+
- attemptNumber (number)
|
|
84
|
+
- error (string)
|
|
85
|
+
- backoffMs (number)
|
|
86
|
+
|
|
87
|
+
**Retorna**: Promise<Object> - Registro insertado
|
|
88
|
+
|
|
89
|
+
**Ejemplo**:
|
|
90
|
+
```javascript
|
|
91
|
+
const retryEvent = await databaseService.insertRetryEvent(
|
|
92
|
+
'upload-event-id-456',
|
|
93
|
+
'session-123',
|
|
94
|
+
{
|
|
95
|
+
timestamp: '2025-11-14T10:31:00Z',
|
|
96
|
+
attemptNumber: 1,
|
|
97
|
+
error: 'Connection timeout',
|
|
98
|
+
backoffMs: 1000
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Tabla watch_events**:
|
|
104
|
+
```sql
|
|
105
|
+
{
|
|
106
|
+
id: UUID (auto-generated),
|
|
107
|
+
upload_event_id: UUID,
|
|
108
|
+
session_id: VARCHAR,
|
|
109
|
+
timestamp: TIMESTAMP,
|
|
110
|
+
attempt_number: INTEGER,
|
|
111
|
+
error_message: VARCHAR,
|
|
112
|
+
backoff_ms: INTEGER,
|
|
113
|
+
type: VARCHAR ('retry', 'error', 'warning'),
|
|
114
|
+
created_at: TIMESTAMP (auto)
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### 3. getSessionUploadHistory(sessionId, options)
|
|
121
|
+
**Propósito**: Recuperar historial de uploads para una sesión
|
|
122
|
+
|
|
123
|
+
**Parámetros**:
|
|
124
|
+
- `sessionId` (string): ID de sesión a consultar
|
|
125
|
+
- `options` (Object - opcional):
|
|
126
|
+
- limit (number, default: 100)
|
|
127
|
+
- offset (number, default: 0)
|
|
128
|
+
- strategy (string, optional - filter)
|
|
129
|
+
|
|
130
|
+
**Retorna**: Promise<Array> - Array de eventos de upload
|
|
131
|
+
|
|
132
|
+
**Ejemplo**:
|
|
133
|
+
```javascript
|
|
134
|
+
// Obtener últimos 20 uploads de la sesión
|
|
135
|
+
const uploads = await databaseService.getSessionUploadHistory('session-123', {
|
|
136
|
+
limit: 20,
|
|
137
|
+
offset: 0,
|
|
138
|
+
strategy: 'batch' // opcional
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Resultado:
|
|
142
|
+
// [
|
|
143
|
+
// {
|
|
144
|
+
// id: '...',
|
|
145
|
+
// session_id: 'session-123',
|
|
146
|
+
// timestamp: '2025-11-14T10:30:00Z',
|
|
147
|
+
// strategy: 'batch',
|
|
148
|
+
// file_count: 20,
|
|
149
|
+
// success_count: 18,
|
|
150
|
+
// failure_count: 2,
|
|
151
|
+
// retry_count: 3,
|
|
152
|
+
// duration_ms: 5000,
|
|
153
|
+
// status: 'completed'
|
|
154
|
+
// },
|
|
155
|
+
// ...
|
|
156
|
+
// ]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### 4. getUploadRetryHistory(uploadEventId, options)
|
|
162
|
+
**Propósito**: Recuperar historial de reintentos para un upload específico
|
|
163
|
+
|
|
164
|
+
**Parámetros**:
|
|
165
|
+
- `uploadEventId` (string): ID del evento de upload
|
|
166
|
+
- `options` (Object - opcional):
|
|
167
|
+
- limit (number, default: 100)
|
|
168
|
+
- offset (number, default: 0)
|
|
169
|
+
|
|
170
|
+
**Retorna**: Promise<Array> - Array de eventos de reintento
|
|
171
|
+
|
|
172
|
+
**Ejemplo**:
|
|
173
|
+
```javascript
|
|
174
|
+
const retries = await databaseService.getUploadRetryHistory('upload-event-id-456', {
|
|
175
|
+
limit: 10,
|
|
176
|
+
offset: 0
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Resultado:
|
|
180
|
+
// [
|
|
181
|
+
// {
|
|
182
|
+
// id: '...',
|
|
183
|
+
// upload_event_id: 'upload-event-id-456',
|
|
184
|
+
// timestamp: '2025-11-14T10:31:00Z',
|
|
185
|
+
// attempt_number: 1,
|
|
186
|
+
// error_message: 'Connection timeout',
|
|
187
|
+
// backoff_ms: 1000,
|
|
188
|
+
// type: 'retry'
|
|
189
|
+
// },
|
|
190
|
+
// {
|
|
191
|
+
// id: '...',
|
|
192
|
+
// upload_event_id: 'upload-event-id-456',
|
|
193
|
+
// timestamp: '2025-11-14T10:31:02Z',
|
|
194
|
+
// attempt_number: 2,
|
|
195
|
+
// error_message: null,
|
|
196
|
+
// backoff_ms: 0,
|
|
197
|
+
// type: 'retry'
|
|
198
|
+
// },
|
|
199
|
+
// ...
|
|
200
|
+
// ]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
### 5. getSessionStatistics(sessionId)
|
|
206
|
+
**Propósito**: Calcular estadísticas completas de una sesión
|
|
207
|
+
|
|
208
|
+
**Parámetros**:
|
|
209
|
+
- `sessionId` (string): ID de sesión a analizar
|
|
210
|
+
|
|
211
|
+
**Retorna**: Promise<Object> - Objeto con estadísticas completas
|
|
212
|
+
|
|
213
|
+
**Estructura Retornada**:
|
|
214
|
+
```javascript
|
|
215
|
+
{
|
|
216
|
+
sessionId: 'session-123',
|
|
217
|
+
totalUploadEvents: 15,
|
|
218
|
+
totalRetryEvents: 8,
|
|
219
|
+
totalFileCount: 250,
|
|
220
|
+
totalSuccessCount: 245,
|
|
221
|
+
totalFailureCount: 5,
|
|
222
|
+
totalRetryCount: 8,
|
|
223
|
+
totalDuration: 45000,
|
|
224
|
+
byStrategy: {
|
|
225
|
+
individual: {
|
|
226
|
+
uploadCount: 5,
|
|
227
|
+
totalFiles: 50,
|
|
228
|
+
totalSuccess: 48,
|
|
229
|
+
totalFailure: 2,
|
|
230
|
+
successRate: 96.00,
|
|
231
|
+
totalDuration: 15000
|
|
232
|
+
},
|
|
233
|
+
batch: {
|
|
234
|
+
uploadCount: 8,
|
|
235
|
+
totalFiles: 150,
|
|
236
|
+
totalSuccess: 145,
|
|
237
|
+
totalFailure: 5,
|
|
238
|
+
successRate: 96.67,
|
|
239
|
+
totalDuration: 20000
|
|
240
|
+
},
|
|
241
|
+
'full-structure': {
|
|
242
|
+
uploadCount: 2,
|
|
243
|
+
totalFiles: 50,
|
|
244
|
+
totalSuccess: 52,
|
|
245
|
+
totalFailure: 0,
|
|
246
|
+
successRate: 100.00,
|
|
247
|
+
totalDuration: 10000
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
retryStats: {
|
|
251
|
+
totalRetries: 8,
|
|
252
|
+
uniqueUploadsWithRetries: 4,
|
|
253
|
+
totalRetryDuration: 7000
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Ejemplo de Uso**:
|
|
259
|
+
```javascript
|
|
260
|
+
const stats = await databaseService.getSessionStatistics('session-123');
|
|
261
|
+
console.log(`Tasa de éxito: ${(stats.totalSuccessCount / (stats.totalSuccessCount + stats.totalFailureCount) * 100).toFixed(2)}%`);
|
|
262
|
+
console.log(`Estrategia más usada: batch (${stats.byStrategy.batch.uploadCount} uploads)`);
|
|
263
|
+
console.log(`Uploads con reintentos: ${stats.retryStats.uniqueUploadsWithRetries}`);
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
### 6. cleanupOldSessions(daysOld)
|
|
269
|
+
**Propósito**: Eliminar datos de sesiones antiguas (limpieza)
|
|
270
|
+
|
|
271
|
+
**Parámetros**:
|
|
272
|
+
- `daysOld` (number, default: 30): Eliminar sesiones más antiguas que esto
|
|
273
|
+
|
|
274
|
+
**Retorna**: Promise<Object>
|
|
275
|
+
```javascript
|
|
276
|
+
{
|
|
277
|
+
deletedUploads: number,
|
|
278
|
+
deletedEvents: number,
|
|
279
|
+
sessionsDeleted: number
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Ejemplo**:
|
|
284
|
+
```javascript
|
|
285
|
+
// Eliminar sesiones más antiguas de 30 días
|
|
286
|
+
const result = await databaseService.cleanupOldSessions(30);
|
|
287
|
+
console.log(`Eliminadas ${result.sessionsDeleted} sesiones antiguas`);
|
|
288
|
+
console.log(`- ${result.deletedUploads} registros de upload`);
|
|
289
|
+
console.log(`- ${result.deletedEvents} eventos`);
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## 🔌 Integración con LoggingService
|
|
295
|
+
|
|
296
|
+
### Flujo de Datos
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
WatchCommand
|
|
300
|
+
↓
|
|
301
|
+
uploadIndividual/uploadBatch/uploadFullStructure
|
|
302
|
+
↓
|
|
303
|
+
logger.recordUploadEvent(uploadEvent) ← LoggingService
|
|
304
|
+
↓
|
|
305
|
+
databaseService.insertUploadEvent(...) ← DatabaseService
|
|
306
|
+
↓
|
|
307
|
+
watch_uploads table
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Ejemplo de Integración Completa
|
|
311
|
+
|
|
312
|
+
```javascript
|
|
313
|
+
import databaseService from './DatabaseService.js';
|
|
314
|
+
import logger from './LoggingService.js';
|
|
315
|
+
|
|
316
|
+
// En WatchCommand.js
|
|
317
|
+
const sessionId = randomUUID();
|
|
318
|
+
logger.initializeSession(sessionId);
|
|
319
|
+
|
|
320
|
+
// Durante upload
|
|
321
|
+
const uploadEventId = logger.recordUploadEvent({
|
|
322
|
+
strategy: 'batch',
|
|
323
|
+
fileCount: 20,
|
|
324
|
+
successCount: 18,
|
|
325
|
+
failureCount: 2,
|
|
326
|
+
duration: 5000
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Guardar en BD
|
|
330
|
+
const dbRecord = await databaseService.insertUploadEvent(
|
|
331
|
+
{
|
|
332
|
+
strategy: 'batch',
|
|
333
|
+
fileCount: 20,
|
|
334
|
+
successCount: 18,
|
|
335
|
+
failureCount: 2,
|
|
336
|
+
duration: 5000
|
|
337
|
+
},
|
|
338
|
+
sessionId
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
// En reintento
|
|
342
|
+
logger.recordRetryEvent(
|
|
343
|
+
uploadEventId,
|
|
344
|
+
1,
|
|
345
|
+
'Connection timeout',
|
|
346
|
+
1000
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
// Guardar retry en BD
|
|
350
|
+
await databaseService.insertRetryEvent(
|
|
351
|
+
dbRecord.id,
|
|
352
|
+
sessionId,
|
|
353
|
+
{
|
|
354
|
+
timestamp: new Date().toISOString(),
|
|
355
|
+
attemptNumber: 1,
|
|
356
|
+
error: 'Connection timeout',
|
|
357
|
+
backoffMs: 1000
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
// Al finalizar
|
|
362
|
+
const stats = await databaseService.getSessionStatistics(sessionId);
|
|
363
|
+
const report = logger.formatSessionReport();
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## 🛡️ Características de Robustez
|
|
369
|
+
|
|
370
|
+
### Retry Logic
|
|
371
|
+
Todos los métodos de DatabaseService utilizan `#queryWithRetry()` con:
|
|
372
|
+
- Exponential backoff (1s, 2s, 4s, ... max 30s)
|
|
373
|
+
- Detección automática de errores retriables
|
|
374
|
+
- Logging detallado de reintentos
|
|
375
|
+
|
|
376
|
+
### Error Handling
|
|
377
|
+
- Try-catch blocks en todos los métodos
|
|
378
|
+
- Logging de errores con contexto
|
|
379
|
+
- Devolución de valores por defecto en caso de falla
|
|
380
|
+
- No lanzamiento de excepciones no capturadas
|
|
381
|
+
|
|
382
|
+
### Performance
|
|
383
|
+
- Batch queries para operaciones grandes
|
|
384
|
+
- Pagination support (limit/offset)
|
|
385
|
+
- Índices recomendados en session_id y timestamp
|
|
386
|
+
- Cleanup automática de datos antiguos
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## 📊 Rendimiento Estimado
|
|
391
|
+
|
|
392
|
+
### Inserciones
|
|
393
|
+
- insertUploadEvent(): ~100-200ms (con retry)
|
|
394
|
+
- insertRetryEvent(): ~50-100ms (con retry)
|
|
395
|
+
|
|
396
|
+
### Consultas
|
|
397
|
+
- getSessionUploadHistory(): ~200-500ms (depende del rango)
|
|
398
|
+
- getUploadRetryHistory(): ~100-300ms
|
|
399
|
+
- getSessionStatistics(): ~500ms-1s (con aggregations)
|
|
400
|
+
|
|
401
|
+
### Cleanup
|
|
402
|
+
- cleanupOldSessions(30): ~1-2s (depende del volumen)
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## 🗄️ Esquema SQL Recomendado
|
|
407
|
+
|
|
408
|
+
### Tabla watch_uploads
|
|
409
|
+
```sql
|
|
410
|
+
CREATE TABLE watch_uploads (
|
|
411
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
412
|
+
session_id VARCHAR(36) NOT NULL,
|
|
413
|
+
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
414
|
+
strategy VARCHAR(20) NOT NULL, -- 'individual', 'batch', 'full-structure'
|
|
415
|
+
file_count INTEGER NOT NULL DEFAULT 0,
|
|
416
|
+
success_count INTEGER NOT NULL DEFAULT 0,
|
|
417
|
+
failure_count INTEGER NOT NULL DEFAULT 0,
|
|
418
|
+
retry_count INTEGER NOT NULL DEFAULT 0,
|
|
419
|
+
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
420
|
+
status VARCHAR(20) DEFAULT 'completed',
|
|
421
|
+
metadata JSONB,
|
|
422
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
423
|
+
|
|
424
|
+
CONSTRAINT fk_session FOREIGN KEY (session_id)
|
|
425
|
+
REFERENCES watch_sessions(id) ON DELETE CASCADE
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
CREATE INDEX idx_watch_uploads_session_id ON watch_uploads(session_id);
|
|
429
|
+
CREATE INDEX idx_watch_uploads_timestamp ON watch_uploads(timestamp);
|
|
430
|
+
CREATE INDEX idx_watch_uploads_strategy ON watch_uploads(strategy);
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Tabla watch_events
|
|
434
|
+
```sql
|
|
435
|
+
CREATE TABLE watch_events (
|
|
436
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
437
|
+
upload_event_id UUID,
|
|
438
|
+
session_id VARCHAR(36) NOT NULL,
|
|
439
|
+
timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
440
|
+
attempt_number INTEGER,
|
|
441
|
+
error_message VARCHAR(500),
|
|
442
|
+
backoff_ms INTEGER DEFAULT 0,
|
|
443
|
+
type VARCHAR(20) NOT NULL, -- 'retry', 'error', 'warning', 'info'
|
|
444
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
445
|
+
|
|
446
|
+
CONSTRAINT fk_upload_event FOREIGN KEY (upload_event_id)
|
|
447
|
+
REFERENCES watch_uploads(id) ON DELETE CASCADE,
|
|
448
|
+
CONSTRAINT fk_session FOREIGN KEY (session_id)
|
|
449
|
+
REFERENCES watch_sessions(id) ON DELETE CASCADE
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
CREATE INDEX idx_watch_events_upload_event_id ON watch_events(upload_event_id);
|
|
453
|
+
CREATE INDEX idx_watch_events_session_id ON watch_events(session_id);
|
|
454
|
+
CREATE INDEX idx_watch_events_timestamp ON watch_events(timestamp);
|
|
455
|
+
CREATE INDEX idx_watch_events_type ON watch_events(type);
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Tabla watch_sessions
|
|
459
|
+
```sql
|
|
460
|
+
CREATE TABLE watch_sessions (
|
|
461
|
+
id VARCHAR(36) PRIMARY KEY,
|
|
462
|
+
start_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
463
|
+
end_time TIMESTAMP,
|
|
464
|
+
user_id VARCHAR(255),
|
|
465
|
+
status VARCHAR(20) DEFAULT 'active', -- 'active', 'completed', 'error'
|
|
466
|
+
total_uploads INTEGER DEFAULT 0,
|
|
467
|
+
total_retries INTEGER DEFAULT 0,
|
|
468
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
CREATE INDEX idx_watch_sessions_start_time ON watch_sessions(start_time);
|
|
472
|
+
CREATE INDEX idx_watch_sessions_status ON watch_sessions(status);
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## 🔄 Uso en WatchCommand
|
|
478
|
+
|
|
479
|
+
El siguiente paso será integrar estos métodos en `WatchCommand.js`:
|
|
480
|
+
|
|
481
|
+
```javascript
|
|
482
|
+
// En execute() method
|
|
483
|
+
const sessionId = randomUUID();
|
|
484
|
+
logger.initializeSession(sessionId);
|
|
485
|
+
|
|
486
|
+
// En cada upload
|
|
487
|
+
const uploadEventId = logger.recordUploadEvent({
|
|
488
|
+
strategy: uploadStrategy,
|
|
489
|
+
fileCount: files.length,
|
|
490
|
+
successCount: successCount,
|
|
491
|
+
failureCount: failureCount,
|
|
492
|
+
duration: endTime - startTime
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// Guardar en BD
|
|
496
|
+
await databaseService.insertUploadEvent(
|
|
497
|
+
{ /* event data */ },
|
|
498
|
+
sessionId
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
// En reintentos
|
|
502
|
+
logger.recordRetryEvent(uploadEventId, attemptNumber, error.message, backoffMs);
|
|
503
|
+
await databaseService.insertRetryEvent(uploadEventId, sessionId, { /* retry data */ });
|
|
504
|
+
|
|
505
|
+
// Al finalizar
|
|
506
|
+
const stats = await databaseService.getSessionStatistics(sessionId);
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
## ✅ Validación Completada
|
|
512
|
+
|
|
513
|
+
✅ **Sintaxis**: Verificada con `node -c`
|
|
514
|
+
✅ **Métodos**: 6 nuevos métodos implementados
|
|
515
|
+
✅ **Error Handling**: Try-catch en todos los métodos
|
|
516
|
+
✅ **Retry Logic**: Usa `#queryWithRetry()` existente
|
|
517
|
+
✅ **Logging**: Logging en todos los puntos críticos
|
|
518
|
+
✅ **Integración**: Compatible con LoggingService
|
|
519
|
+
|
|
520
|
+
---
|
|
521
|
+
|
|
522
|
+
## 📈 Impacto de Cambios
|
|
523
|
+
|
|
524
|
+
```
|
|
525
|
+
Líneas nuevas: 386
|
|
526
|
+
Métodos nuevos: 6
|
|
527
|
+
Propiedades nuevas: 0
|
|
528
|
+
Funcionalidades: Persistencia, Consultas, Estadísticas, Limpieza
|
|
529
|
+
Compatibilidad: ✅ Hacia atrás
|
|
530
|
+
Breaking Changes: ❌ Ninguno
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
## 🚀 Próximos Pasos
|
|
536
|
+
|
|
537
|
+
1. ✅ **DatabaseService mejorado** - COMPLETADO
|
|
538
|
+
2. ⏳ **Integración en WatchCommand** - Siguiente
|
|
539
|
+
3. ⏳ **Progress Bars** - Después
|
|
540
|
+
4. ⏳ **Session Summary Report** - Final Fase 5
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
**Status**: DatabaseService completamente mejorado
|
|
545
|
+
**Última actualización**: 14 de Noviembre, 2025
|
|
546
|
+
**Validación**: ✅ Sintaxis correcta, 2078 líneas totales
|