cdp-edge 1.2.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.
Files changed (128) hide show
  1. package/README.md +367 -0
  2. package/bin/cdp-edge.js +61 -0
  3. package/contracts/api-versions.json +368 -0
  4. package/dist/commands/analyze.js +52 -0
  5. package/dist/commands/infra.js +54 -0
  6. package/dist/commands/install.js +168 -0
  7. package/dist/commands/server.js +174 -0
  8. package/dist/commands/setup.js +123 -0
  9. package/dist/commands/validate.js +84 -0
  10. package/dist/index.js +12 -0
  11. package/docs/CI-CD-SETUP.md +217 -0
  12. package/docs/PixelBuilder-Documentacao-Completa (2).docx +0 -0
  13. package/docs/events-reference.md +359 -0
  14. package/docs/installation.md +155 -0
  15. package/docs/quick-start.md +185 -0
  16. package/docs/sdk-reference.md +371 -0
  17. package/docs/whatsapp-ctwa.md +209 -0
  18. package/extracted-skill/tracking-events-generator/INDEX.md +94 -0
  19. package/extracted-skill/tracking-events-generator/INSTALACAO-CDPEDGE.md +58 -0
  20. package/extracted-skill/tracking-events-generator/INTEGRACAO-COMPLETA.md +594 -0
  21. package/extracted-skill/tracking-events-generator/MELHORIAS-IMPLEMENTADAS.md +412 -0
  22. package/extracted-skill/tracking-events-generator/Premium-Tracking-Intelligence-Resumo.md +333 -0
  23. package/extracted-skill/tracking-events-generator/SKILL.md +257 -0
  24. package/extracted-skill/tracking-events-generator/advanced-matching.js +364 -0
  25. package/extracted-skill/tracking-events-generator/agents/ab-testing-agent.md +54 -0
  26. package/extracted-skill/tracking-events-generator/agents/attribution-agent.md +1304 -0
  27. package/extracted-skill/tracking-events-generator/agents/bing-agent.md +76 -0
  28. package/extracted-skill/tracking-events-generator/agents/browser-tracking.md +264 -0
  29. package/extracted-skill/tracking-events-generator/agents/code-guardian-agent.md +149 -0
  30. package/extracted-skill/tracking-events-generator/agents/compliance-agent.md +2077 -0
  31. package/extracted-skill/tracking-events-generator/agents/crm-integration-agent.md +1419 -0
  32. package/extracted-skill/tracking-events-generator/agents/dashboard-agent.md +456 -0
  33. package/extracted-skill/tracking-events-generator/agents/database-agent.md +667 -0
  34. package/extracted-skill/tracking-events-generator/agents/debug-agent.md +1455 -0
  35. package/extracted-skill/tracking-events-generator/agents/domain-setup-agent.md +224 -0
  36. package/extracted-skill/tracking-events-generator/agents/email-agent.md +61 -0
  37. package/extracted-skill/tracking-events-generator/agents/fingerprint-agent.md +52 -0
  38. package/extracted-skill/tracking-events-generator/agents/google-agent.md +109 -0
  39. package/extracted-skill/tracking-events-generator/agents/intelligence-agent.md +365 -0
  40. package/extracted-skill/tracking-events-generator/agents/intelligence-scheduling.md +643 -0
  41. package/extracted-skill/tracking-events-generator/agents/linkedin-agent.md +62 -0
  42. package/extracted-skill/tracking-events-generator/agents/localization-agent.md +55 -0
  43. package/extracted-skill/tracking-events-generator/agents/ltv-predictor-agent.md +59 -0
  44. package/extracted-skill/tracking-events-generator/agents/master-feedback-loop.md +900 -0
  45. package/extracted-skill/tracking-events-generator/agents/master-orchestrator.md +1922 -0
  46. package/extracted-skill/tracking-events-generator/agents/memory-agent.json +109 -0
  47. package/extracted-skill/tracking-events-generator/agents/memory-agent.md +703 -0
  48. package/extracted-skill/tracking-events-generator/agents/meta-agent.md +110 -0
  49. package/extracted-skill/tracking-events-generator/agents/page-analyzer.md +255 -0
  50. package/extracted-skill/tracking-events-generator/agents/performance-agent.md +1157 -0
  51. package/extracted-skill/tracking-events-generator/agents/performance-optimization-agent.md +1432 -0
  52. package/extracted-skill/tracking-events-generator/agents/pinterest-agent.md +310 -0
  53. package/extracted-skill/tracking-events-generator/agents/premium-tracking-intelligence-agent.md +849 -0
  54. package/extracted-skill/tracking-events-generator/agents/r2-setup-agent.md +250 -0
  55. package/extracted-skill/tracking-events-generator/agents/reddit-agent.md +313 -0
  56. package/extracted-skill/tracking-events-generator/agents/security-enterprise-agent.md +1752 -0
  57. package/extracted-skill/tracking-events-generator/agents/server-tracking.md +1188 -0
  58. package/extracted-skill/tracking-events-generator/agents/spotify-agent.md +383 -0
  59. package/extracted-skill/tracking-events-generator/agents/tiktok-agent.md +111 -0
  60. package/extracted-skill/tracking-events-generator/agents/tracking-plan-agent.md +364 -0
  61. package/extracted-skill/tracking-events-generator/agents/validator-agent.md +267 -0
  62. package/extracted-skill/tracking-events-generator/agents/webhook-agent.md +69 -0
  63. package/extracted-skill/tracking-events-generator/agents/whatsapp-agent.md +76 -0
  64. package/extracted-skill/tracking-events-generator/agents/whatsapp-ctwa-setup-agent.md +699 -0
  65. package/extracted-skill/tracking-events-generator/agents/youtube-agent.md +422 -0
  66. package/extracted-skill/tracking-events-generator/anti-blocking.js +285 -0
  67. package/extracted-skill/tracking-events-generator/cdpTrack.js +641 -0
  68. package/extracted-skill/tracking-events-generator/contracts/api-versions.json +368 -0
  69. package/extracted-skill/tracking-events-generator/docs/guia-cloudflare-iniciante.md +107 -0
  70. package/extracted-skill/tracking-events-generator/engagement-scoring.js +226 -0
  71. package/extracted-skill/tracking-events-generator/evals/evals.json +235 -0
  72. package/extracted-skill/tracking-events-generator/integration-test.js +497 -0
  73. package/extracted-skill/tracking-events-generator/knowledge-base.md +2894 -0
  74. package/extracted-skill/tracking-events-generator/micro-events.js +992 -0
  75. package/extracted-skill/tracking-events-generator/models/captura-de-lead.md +78 -0
  76. package/extracted-skill/tracking-events-generator/models/captura-lead-evento-externo.md +99 -0
  77. package/extracted-skill/tracking-events-generator/models/checkout-proprio.md +111 -0
  78. package/extracted-skill/tracking-events-generator/models/multi-step-checkout.md +672 -0
  79. package/extracted-skill/tracking-events-generator/models/pagina-obrigado.md +55 -0
  80. package/extracted-skill/tracking-events-generator/models/pinterest/conversions-api-template.js +144 -0
  81. package/extracted-skill/tracking-events-generator/models/pinterest/event-mappings.json +48 -0
  82. package/extracted-skill/tracking-events-generator/models/pinterest/tag-template.js +28 -0
  83. package/extracted-skill/tracking-events-generator/models/quiz-funnel.md +68 -0
  84. package/extracted-skill/tracking-events-generator/models/reddit/conversions-api-template.js +205 -0
  85. package/extracted-skill/tracking-events-generator/models/reddit/event-mappings.json +56 -0
  86. package/extracted-skill/tracking-events-generator/models/reddit/pixel-template.js +19 -0
  87. package/extracted-skill/tracking-events-generator/models/scenarios/behavior-engine.js +425 -0
  88. package/extracted-skill/tracking-events-generator/models/scenarios/real-estate-logic.md +50 -0
  89. package/extracted-skill/tracking-events-generator/models/scenarios/sales-page-logic.md +50 -0
  90. package/extracted-skill/tracking-events-generator/models/trafego-direto.md +582 -0
  91. package/extracted-skill/tracking-events-generator/models/webinar-registration.md +63 -0
  92. package/extracted-skill/tracking-events-generator/tracking.config.js +46 -0
  93. package/extracted-skill/tracking-events-generator/walkthrough.md +26 -0
  94. package/package.json +75 -0
  95. package/server-edge-tracker/INSTALAR.md +328 -0
  96. package/server-edge-tracker/migrate-new-db.sql +137 -0
  97. package/server-edge-tracker/migrate-v2.sql +16 -0
  98. package/server-edge-tracker/migrate-v3.sql +6 -0
  99. package/server-edge-tracker/migrate-v4.sql +18 -0
  100. package/server-edge-tracker/migrate-v5.sql +17 -0
  101. package/server-edge-tracker/migrate-v6.sql +24 -0
  102. package/server-edge-tracker/migrate.sql +111 -0
  103. package/server-edge-tracker/schema.sql +265 -0
  104. package/server-edge-tracker/worker.js +2574 -0
  105. package/server-edge-tracker/wrangler.toml +85 -0
  106. package/templates/afiliado-sem-landing.md +312 -0
  107. package/templates/captura-de-lead.md +78 -0
  108. package/templates/captura-lead-evento-externo.md +99 -0
  109. package/templates/checkout-proprio.md +111 -0
  110. package/templates/install/.claude/commands/cdp.md +1 -0
  111. package/templates/install/CLAUDE.md +65 -0
  112. package/templates/linkedin/tag-template.js +46 -0
  113. package/templates/multi-step-checkout.md +673 -0
  114. package/templates/pagina-obrigado.md +55 -0
  115. package/templates/pinterest/conversions-api-template.js +144 -0
  116. package/templates/pinterest/event-mappings.json +48 -0
  117. package/templates/pinterest/tag-template.js +28 -0
  118. package/templates/quiz-funnel.md +68 -0
  119. package/templates/reddit/conversions-api-template.js +205 -0
  120. package/templates/reddit/event-mappings.json +56 -0
  121. package/templates/reddit/pixel-template.js +46 -0
  122. package/templates/scenarios/behavior-engine.js +402 -0
  123. package/templates/scenarios/real-estate-logic.md +50 -0
  124. package/templates/scenarios/sales-page-logic.md +50 -0
  125. package/templates/spotify/pixel-template.js +46 -0
  126. package/templates/trafego-direto.md +582 -0
  127. package/templates/vsl-page.md +292 -0
  128. package/templates/webinar-registration.md +63 -0
@@ -0,0 +1,1432 @@
1
+ ---
2
+ name: performance-optimization-agent
3
+ description: Performance Optimization Enterprise Agent - Caching, Query Optimization, Latency Profiling for Cloudflare Workers + D1
4
+ type: agent
5
+ persona: Performance Engineer specializing in edge computing optimization, distributed caching strategies, and database query optimization
6
+ version: "1.0.0"
7
+ ---
8
+
9
+ # Performance Optimization Enterprise Agent
10
+
11
+ ## 🚀 Visão Geral
12
+
13
+ Agente especializado em otimização de performance para o sistema CDP Edge (Cloudflare Workers + D1 + Queue). Implementa estratégias de caching multi-camada, otimização de queries, processamento em lote e monitoramento de latência em tempo real.
14
+
15
+ ---
16
+
17
+ ## 📊 Arquitetura de Performance
18
+
19
+ ### 3-Camadas de Caching
20
+
21
+ ```
22
+ ┌─────────────────────────────────────────────────────────────┐
23
+ │ L1: Memory Cache │
24
+ │ (In-Memory, Request Lifetime) │
25
+ │ - TTL: Request duration │
26
+ │ - Hit Rate: ~85% │
27
+ │ - Use: Session data, config lookup │
28
+ └─────────────────────────────────────────────────────────────┘
29
+
30
+ ┌─────────────────────────────────────────────────────────────┐
31
+ │ L2: KV Cache │
32
+ │ (Global, Distributed Edge) │
33
+ │ - TTL: 5-60 minutes │
34
+ │ - Hit Rate: ~12% │
35
+ │ - Use: Attribution data, channel metrics │
36
+ └─────────────────────────────────────────────────────────────┘
37
+
38
+ ┌─────────────────────────────────────────────────────────────┐
39
+ │ L3: D1 Database │
40
+ │ (SQLite at Edge, Persistent) │
41
+ │ - TTL: Permanent │
42
+ │ - Use: User journeys, audit logs, security data │
43
+ └─────────────────────────────────────────────────────────────┘
44
+ ```
45
+
46
+ ---
47
+
48
+ ## 🎯 Métricas de Performance
49
+
50
+ ### KPIs Monitorados
51
+
52
+ | Métrica | Target | Alert |
53
+ |---------|--------|-------|
54
+ | **Cache Hit Rate (L1+L2)** | ≥ 95% | < 85% |
55
+ | **P95 Latency (Tracking)** | < 100ms | > 200ms |
56
+ | **P95 Latency (Attribution)** | < 500ms | > 1000ms |
57
+ | **Query Time (D1)** | < 50ms | > 100ms |
58
+ | **Queue Processing Time** | < 500ms | > 2000ms |
59
+ | **Memory Usage (Worker)** | < 128MB | > 128MB |
60
+ | **CPU Time (Worker)** | < 50ms | > 150ms |
61
+
62
+ ---
63
+
64
+ ## 💾 Caching Multi-Layer
65
+
66
+ ### L1: Memory Cache (Request-Scoped)
67
+
68
+ ```javascript
69
+ /**
70
+ * L1 Memory Cache - In-request cache with automatic expiration
71
+ */
72
+ class L1Cache {
73
+ constructor() {
74
+ this.cache = new Map();
75
+ this.stats = {
76
+ hits: 0,
77
+ misses: 0,
78
+ sets: 0,
79
+ deletes: 0
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Get value from L1 cache
85
+ * @param {string} key - Cache key
86
+ * @returns {any|undefined} Cached value or undefined if not found/expired
87
+ */
88
+ get(key) {
89
+ const entry = this.cache.get(key);
90
+ if (!entry) {
91
+ this.stats.misses++;
92
+ return undefined;
93
+ }
94
+
95
+ // Check expiration
96
+ if (entry.expiry < Date.now()) {
97
+ this.cache.delete(key);
98
+ this.stats.misses++;
99
+ return undefined;
100
+ }
101
+
102
+ this.stats.hits++;
103
+ return entry.value;
104
+ }
105
+
106
+ /**
107
+ * Set value in L1 cache with optional TTL (default: request lifetime)
108
+ * @param {string} key - Cache key
109
+ * @param {any} value - Value to cache
110
+ * @param {number} ttlMs - TTL in milliseconds (optional)
111
+ */
112
+ set(key, value, ttlMs = null) {
113
+ const expiry = ttlMs ? Date.now() + ttlMs : Infinity;
114
+ this.cache.set(key, { value, expiry, createdAt: Date.now() });
115
+ this.stats.sets++;
116
+ }
117
+
118
+ /**
119
+ * Delete value from L1 cache
120
+ * @param {string} key - Cache key
121
+ */
122
+ delete(key) {
123
+ this.cache.delete(key);
124
+ this.stats.deletes++;
125
+ }
126
+
127
+ /**
128
+ * Clear all L1 cache entries
129
+ */
130
+ clear() {
131
+ this.cache.clear();
132
+ }
133
+
134
+ /**
135
+ * Get cache statistics
136
+ * @returns {object} Cache statistics
137
+ */
138
+ getStats() {
139
+ const totalRequests = this.stats.hits + this.stats.misses;
140
+ const hitRate = totalRequests > 0 ? (this.stats.hits / totalRequests) * 100 : 0;
141
+
142
+ return {
143
+ ...this.stats,
144
+ hitRate: hitRate.toFixed(2) + '%',
145
+ cacheSize: this.cache.size
146
+ };
147
+ }
148
+ }
149
+
150
+ // Global L1 cache instance (request-scoped)
151
+ let l1Cache = new L1Cache();
152
+ ```
153
+
154
+ ### L2: KV Cache (Global Edge Cache)
155
+
156
+ ```javascript
157
+ /**
158
+ * L2 KV Cache - Global distributed cache with automatic invalidation
159
+ */
160
+ class L2Cache {
161
+ constructor(env) {
162
+ this.kv = env.CACHE_KV; // Cloudflare KV namespace
163
+ this.stats = {
164
+ hits: 0,
165
+ misses: 0,
166
+ sets: 0,
167
+ deletes: 0
168
+ };
169
+
170
+ // Cache TTLs (configurable)
171
+ this.ttls = {
172
+ attribution: 3600, // 1 hour
173
+ channelMetrics: 1800, // 30 minutes
174
+ userJourneys: 300, // 5 minutes
175
+ securityConfig: 900, // 15 minutes
176
+ lookupData: 600, // 10 minutes
177
+ default: 600 // 10 minutes
178
+ };
179
+ }
180
+
181
+ /**
182
+ * Get value from L2 cache
183
+ * @param {string} key - Cache key
184
+ * @param {string} cacheType - Type of cache (determines TTL)
185
+ * @returns {Promise<any|null>} Cached value or null if not found
186
+ */
187
+ async get(key, cacheType = 'default') {
188
+ try {
189
+ const cached = await this.kv.get(key, { type: 'json' });
190
+
191
+ if (cached === null) {
192
+ this.stats.misses++;
193
+ return null;
194
+ }
195
+
196
+ this.stats.hits++;
197
+ return cached;
198
+ } catch (error) {
199
+ console.error(`[L2 Cache] Error getting ${key}:`, error);
200
+ this.stats.misses++;
201
+ return null;
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Set value in L2 cache with type-based TTL
207
+ * @param {string} key - Cache key
208
+ * @param {any} value - Value to cache
209
+ * @param {string} cacheType - Type of cache (determines TTL)
210
+ * @param {number} ttlMs - Custom TTL override (optional)
211
+ * @returns {Promise<boolean>} Success status
212
+ */
213
+ async set(key, value, cacheType = 'default', ttlMs = null) {
214
+ try {
215
+ const ttl = ttlMs || (this.ttls[cacheType] || this.ttls.default);
216
+
217
+ await this.kv.put(key, JSON.stringify(value), {
218
+ expirationTtl: ttl
219
+ });
220
+
221
+ this.stats.sets++;
222
+ return true;
223
+ } catch (error) {
224
+ console.error(`[L2 Cache] Error setting ${key}:`, error);
225
+ return false;
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Delete value from L2 cache
231
+ * @param {string} key - Cache key
232
+ * @returns {Promise<boolean>} Success status
233
+ */
234
+ async delete(key) {
235
+ try {
236
+ await this.kv.delete(key);
237
+ this.stats.deletes++;
238
+ return true;
239
+ } catch (error) {
240
+ console.error(`[L2 Cache] Error deleting ${key}:`, error);
241
+ return false;
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Delete multiple keys from L2 cache (batch invalidation)
247
+ * @param {string[]} keys - Array of cache keys
248
+ * @returns {Promise<number>} Number of successfully deleted keys
249
+ */
250
+ async deleteMany(keys) {
251
+ let deleted = 0;
252
+ for (const key of keys) {
253
+ if (await this.delete(key)) {
254
+ deleted++;
255
+ }
256
+ }
257
+ return deleted;
258
+ }
259
+
260
+ /**
261
+ * List keys by prefix (for cache invalidation)
262
+ * @param {string} prefix - Key prefix
263
+ * @param {number} limit - Maximum keys to return
264
+ * @returns {Promise<string[]>} Array of keys
265
+ */
266
+ async listKeys(prefix, limit = 100) {
267
+ try {
268
+ const list = await this.kv.list({ prefix, limit });
269
+ return list.keys.map(k => k.name);
270
+ } catch (error) {
271
+ console.error(`[L2 Cache] Error listing keys with prefix ${prefix}:`, error);
272
+ return [];
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Invalidate cache by prefix (bulk invalidation)
278
+ * @param {string} prefix - Key prefix to invalidate
279
+ * @returns {Promise<number>} Number of invalidated keys
280
+ */
281
+ async invalidateByPrefix(prefix) {
282
+ const keys = await this.listKeys(prefix, 1000);
283
+ return await this.deleteMany(keys);
284
+ }
285
+
286
+ /**
287
+ * Get cache statistics
288
+ * @returns {object} Cache statistics
289
+ */
290
+ getStats() {
291
+ const totalRequests = this.stats.hits + this.stats.misses;
292
+ const hitRate = totalRequests > 0 ? (this.stats.hits / totalRequests) * 100 : 0;
293
+
294
+ return {
295
+ ...this.stats,
296
+ hitRate: hitRate.toFixed(2) + '%'
297
+ };
298
+ }
299
+ }
300
+
301
+ // L2 cache instance (environment-scoped)
302
+ let l2Cache = null;
303
+ ```
304
+
305
+ ### Cache Strategy Implementation
306
+
307
+ ```javascript
308
+ /**
309
+ * Multi-Layer Cache Manager
310
+ * Orchestrates L1 and L2 caches with fallback logic
311
+ */
312
+ class CacheManager {
313
+ constructor(env) {
314
+ this.l1 = new L1Cache();
315
+ this.l2 = new L2Cache(env);
316
+ }
317
+
318
+ /**
319
+ * Get value from cache (L1 → L2 → D1 fallback)
320
+ * @param {string} key - Cache key
321
+ * @param {string} cacheType - Type of cache
322
+ * @param {Function} fallback - Fallback function to fetch from D1
323
+ * @returns {Promise<any>} Cached or fetched value
324
+ */
325
+ async get(key, cacheType = 'default', fallback = null) {
326
+ // Try L1 cache first (fastest)
327
+ const l1Value = this.l1.get(key);
328
+ if (l1Value !== undefined) {
329
+ return l1Value;
330
+ }
331
+
332
+ // Try L2 cache (distributed)
333
+ const l2Value = await this.l2.get(key, cacheType);
334
+ if (l2Value !== null) {
335
+ // Promote to L1 cache
336
+ this.l1.set(key, l2Value);
337
+ return l2Value;
338
+ }
339
+
340
+ // Fallback to source (D1)
341
+ if (fallback) {
342
+ const value = await fallback();
343
+
344
+ // Cache in L2 and L1
345
+ await this.l2.set(key, value, cacheType);
346
+ this.l1.set(key, value);
347
+
348
+ return value;
349
+ }
350
+
351
+ return null;
352
+ }
353
+
354
+ /**
355
+ * Set value in cache (L1 + L2)
356
+ * @param {string} key - Cache key
357
+ * @param {any} value - Value to cache
358
+ * @param {string} cacheType - Type of cache
359
+ * @param {number} ttlMs - Custom TTL for L2
360
+ */
361
+ async set(key, value, cacheType = 'default', ttlMs = null) {
362
+ this.l1.set(key, value);
363
+ await this.l2.set(key, value, cacheType, ttlMs);
364
+ }
365
+
366
+ /**
367
+ * Delete value from cache (L1 + L2)
368
+ * @param {string} key - Cache key
369
+ */
370
+ async delete(key) {
371
+ this.l1.delete(key);
372
+ await this.l2.delete(key);
373
+ }
374
+
375
+ /**
376
+ * Invalidate cache by prefix (L2 only - L1 doesn't support prefix)
377
+ * @param {string} prefix - Key prefix
378
+ */
379
+ async invalidateByPrefix(prefix) {
380
+ await this.l2.invalidateByPrefix(prefix);
381
+ }
382
+
383
+ /**
384
+ * Get combined cache statistics
385
+ * @returns {object} Combined cache statistics
386
+ */
387
+ getStats() {
388
+ return {
389
+ l1: this.l1.getStats(),
390
+ l2: this.l2.getStats()
391
+ };
392
+ }
393
+ }
394
+
395
+ // Global cache manager instance
396
+ let cacheManager = null;
397
+ ```
398
+
399
+ ---
400
+
401
+ ## 🗄️ Otimização de Queries D1
402
+
403
+ ### Schema de Índices Otimizados
404
+
405
+ ```sql
406
+ -- User Journeys Table (Optimized for attribution queries)
407
+ CREATE TABLE IF NOT EXISTS user_journeys (
408
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
409
+ user_id TEXT NOT NULL,
410
+ event_id TEXT,
411
+ event_name TEXT NOT NULL,
412
+ utm_source TEXT,
413
+ utm_medium TEXT,
414
+ utm_campaign TEXT,
415
+ utm_term TEXT,
416
+ utm_content TEXT,
417
+ event_timestamp DATETIME NOT NULL,
418
+ position INTEGER,
419
+ conversion_id TEXT,
420
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
421
+ );
422
+
423
+ -- Compound indexes for common query patterns
424
+ CREATE INDEX IF NOT EXISTS idx_user_journeys_user_timestamp
425
+ ON user_journeys(user_id, event_timestamp DESC);
426
+
427
+ CREATE INDEX IF NOT EXISTS idx_user_journeys_conversion
428
+ ON user_journeys(conversion_id, position);
429
+
430
+ CREATE INDEX IF NOT EXISTS idx_user_journeys_utm_source
431
+ ON user_journeys(utm_source, event_timestamp DESC);
432
+
433
+ CREATE INDEX IF NOT EXISTS idx_user_journeys_event_name
434
+ ON user_journeys(event_name, event_timestamp DESC);
435
+
436
+ -- Multi-Touch Attribution Table
437
+ CREATE TABLE IF NOT EXISTS multi_touch_attribution (
438
+ conversion_id TEXT NOT NULL,
439
+ attribution_model TEXT NOT NULL,
440
+ touchpoint_index INTEGER NOT NULL,
441
+ utm_source TEXT,
442
+ credit_percentage REAL NOT NULL,
443
+ role TEXT,
444
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
445
+ PRIMARY KEY (conversion_id, attribution_model, touchpoint_index)
446
+ );
447
+
448
+ -- Index for attribution queries
449
+ CREATE INDEX IF NOT EXISTS idx_attribution_model_conversion
450
+ ON multi_touch_attribution(attribution_model, conversion_id);
451
+
452
+ CREATE INDEX IF NOT EXISTS idx_attribution_source
453
+ ON multi_touch_attribution(utm_source, attribution_model);
454
+
455
+ -- Channel Performance Table (Materialized for fast aggregation)
456
+ CREATE TABLE IF NOT EXISTS channel_performance (
457
+ utm_source TEXT NOT NULL,
458
+ attribution_model TEXT NOT NULL,
459
+ total_attribution REAL NOT NULL,
460
+ total_conversions INTEGER NOT NULL,
461
+ total_value REAL NOT NULL,
462
+ avg_attribution_per_conversion REAL NOT NULL,
463
+ last_updated DATETIME NOT NULL,
464
+ PRIMARY KEY (utm_source, attribution_model)
465
+ );
466
+
467
+ -- Index for performance queries
468
+ CREATE INDEX IF NOT EXISTS idx_channel_performance_model
469
+ ON channel_performance(attribution_model, total_attribution DESC);
470
+
471
+ -- Index for timestamp-based queries
472
+ CREATE INDEX IF NOT EXISTS idx_channel_performance_updated
473
+ ON channel_performance(last_updated DESC);
474
+ ```
475
+
476
+ ### Query Optimization Patterns
477
+
478
+ ```javascript
479
+ /**
480
+ * Optimized Query Builder for D1
481
+ */
482
+ class QueryOptimizer {
483
+ constructor(db) {
484
+ this.db = db;
485
+ this.queryCache = new Map();
486
+ }
487
+
488
+ /**
489
+ * Execute query with automatic index usage and caching
490
+ * @param {string} query - SQL query with parameter placeholders
491
+ * @param {any[]} params - Query parameters
492
+ * @param {string} cacheKey - Optional cache key
493
+ * @returns {Promise<any>} Query result
494
+ */
495
+ async execute(query, params = [], cacheKey = null) {
496
+ const startTime = performance.now();
497
+
498
+ try {
499
+ // Check query cache if cache key provided
500
+ if (cacheKey && this.queryCache.has(cacheKey)) {
501
+ const cached = this.queryCache.get(cacheKey);
502
+ if (Date.now() - cached.timestamp < 60000) { // 1 min cache
503
+ return cached.result;
504
+ }
505
+ }
506
+
507
+ // Prepare and execute query
508
+ const statement = this.db.prepare(query);
509
+ const result = await statement.bind(...params).all();
510
+
511
+ // Cache result if cache key provided
512
+ if (cacheKey) {
513
+ this.queryCache.set(cacheKey, {
514
+ result: result,
515
+ timestamp: Date.now()
516
+ });
517
+ }
518
+
519
+ const queryTime = performance.now() - startTime;
520
+
521
+ // Log slow queries
522
+ if (queryTime > 100) {
523
+ console.warn(`[Slow Query] ${queryTime.toFixed(2)}ms - ${query.substring(0, 100)}...`);
524
+ }
525
+
526
+ return result;
527
+ } catch (error) {
528
+ const queryTime = performance.now() - startTime;
529
+ console.error(`[Query Error] ${queryTime.toFixed(2)}ms - ${error.message}`);
530
+ throw error;
531
+ }
532
+ }
533
+
534
+ /**
535
+ * Get user journey with optimized index usage
536
+ * @param {string} userId - User ID
537
+ * @param {string} conversionId - Optional conversion ID filter
538
+ * @returns {Promise<Array>} User journey events
539
+ */
540
+ async getUserJourney(userId, conversionId = null) {
541
+ const cacheKey = `journey:${userId}:${conversionId || 'all'}`;
542
+
543
+ const query = conversionId
544
+ ? `
545
+ SELECT * FROM user_journeys
546
+ WHERE user_id = ? AND conversion_id = ?
547
+ ORDER BY event_timestamp ASC
548
+ `
549
+ : `
550
+ SELECT * FROM user_journeys
551
+ WHERE user_id = ?
552
+ ORDER BY event_timestamp DESC
553
+ LIMIT 100
554
+ `;
555
+
556
+ const params = conversionId ? [userId, conversionId] : [userId];
557
+ return await this.execute(query, params, cacheKey);
558
+ }
559
+
560
+ /**
561
+ * Get attribution data with optimized joins
562
+ * @param {string} conversionId - Conversion ID
563
+ * @param {string} attributionModel - Attribution model
564
+ * @returns {Promise<Array>} Attribution data
565
+ */
566
+ async getAttributionData(conversionId, attributionModel) {
567
+ const cacheKey = `attribution:${conversionId}:${attributionModel}`;
568
+
569
+ const query = `
570
+ SELECT
571
+ a.*,
572
+ j.utm_medium,
573
+ j.utm_campaign,
574
+ j.event_name,
575
+ j.event_timestamp
576
+ FROM multi_touch_attribution a
577
+ INNER JOIN user_journeys j ON a.conversion_id = j.conversion_id
578
+ WHERE a.conversion_id = ? AND a.attribution_model = ?
579
+ ORDER BY a.touchpoint_index ASC
580
+ `;
581
+
582
+ return await this.execute(query, [conversionId, attributionModel], cacheKey);
583
+ }
584
+
585
+ /**
586
+ * Get channel performance with pre-aggregated data
587
+ * @param {string} attributionModel - Attribution model
588
+ * @param {number} limit - Limit results
589
+ * @returns {Promise<Array>} Channel performance data
590
+ */
591
+ async getChannelPerformance(attributionModel, limit = 50) {
592
+ const cacheKey = `performance:${attributionModel}:${limit}`;
593
+
594
+ const query = `
595
+ SELECT * FROM channel_performance
596
+ WHERE attribution_model = ?
597
+ ORDER BY total_attribution DESC
598
+ LIMIT ?
599
+ `;
600
+
601
+ return await this.execute(query, [attributionModel, limit], cacheKey);
602
+ }
603
+
604
+ /**
605
+ * Batch insert for high-throughput operations
606
+ * @param {string} table - Table name
607
+ * @param {string[]} columns - Column names
608
+ * @param {any[][]} values - Array of value arrays
609
+ * @returns {Promise<number>} Number of inserted rows
610
+ */
611
+ async batchInsert(table, columns, values) {
612
+ const startTime = performance.now();
613
+
614
+ try {
615
+ const placeholders = columns.map(() => '?').join(',');
616
+ const query = `INSERT INTO ${table} (${columns.join(',')}) VALUES (${placeholders})`;
617
+
618
+ let inserted = 0;
619
+ for (const row of values) {
620
+ await this.db.prepare(query).bind(...row).run();
621
+ inserted++;
622
+ }
623
+
624
+ const queryTime = performance.now() - startTime;
625
+ console.log(`[Batch Insert] ${inserted} rows in ${queryTime.toFixed(2)}ms`);
626
+
627
+ return inserted;
628
+ } catch (error) {
629
+ const queryTime = performance.now() - startTime;
630
+ console.error(`[Batch Insert Error] ${queryTime.toFixed(2)}ms - ${error.message}`);
631
+ throw error;
632
+ }
633
+ }
634
+
635
+ /**
636
+ * Invalidate query cache by pattern
637
+ * @param {string} pattern - Cache key pattern (supports wildcards *)
638
+ */
639
+ invalidateCache(pattern) {
640
+ for (const key of this.queryCache.keys()) {
641
+ if (this.matchesPattern(key, pattern)) {
642
+ this.queryCache.delete(key);
643
+ }
644
+ }
645
+ }
646
+
647
+ /**
648
+ * Match cache key against pattern (supports * wildcard)
649
+ * @param {string} key - Cache key
650
+ * @param {string} pattern - Pattern with * wildcards
651
+ * @returns {boolean} Match result
652
+ */
653
+ matchesPattern(key, pattern) {
654
+ const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
655
+ return regex.test(key);
656
+ }
657
+ }
658
+
659
+ // Global query optimizer instance
660
+ let queryOptimizer = null;
661
+ ```
662
+
663
+ ---
664
+
665
+ ## 🚀 Otimização de Processamento em Lote
666
+
667
+ ### Batch Processing for Attribution
668
+
669
+ ```javascript
670
+ /**
671
+ * Batch Attribution Processor
672
+ * Processes multiple attributions in parallel with batching
673
+ */
674
+ class BatchAttributionProcessor {
675
+ constructor(cacheManager, queryOptimizer) {
676
+ this.cache = cacheManager;
677
+ this.optimizer = queryOptimizer;
678
+ this.batchSize = 50; // Process 50 attributions at once
679
+ this.maxParallel = 5; // Max 5 parallel batches
680
+ }
681
+
682
+ /**
683
+ * Process multiple attributions in batches
684
+ * @param {Array} conversions - Array of conversion IDs
685
+ * @param {string} attributionModel - Attribution model
686
+ * @returns {Promise<Array>} Processing results
687
+ */
688
+ async processBatch(conversions, attributionModel) {
689
+ const results = [];
690
+ const batches = this.chunkArray(conversions, this.batchSize);
691
+
692
+ // Process batches in parallel with concurrency limit
693
+ const batchPromises = batches.map(batch =>
694
+ this.processSingleBatch(batch, attributionModel)
695
+ );
696
+
697
+ const batchResults = await Promise.all(batchPromises);
698
+
699
+ // Flatten results
700
+ for (const batchResult of batchResults) {
701
+ results.push(...batchResult);
702
+ }
703
+
704
+ return results;
705
+ }
706
+
707
+ /**
708
+ * Process a single batch of attributions
709
+ * @param {Array} conversions - Array of conversion IDs
710
+ * @param {string} attributionModel - Attribution model
711
+ * @returns {Promise<Array>} Processing results
712
+ */
713
+ async processSingleBatch(conversions, attributionModel) {
714
+ const results = [];
715
+
716
+ for (const conversionId of conversions) {
717
+ try {
718
+ // Check cache first
719
+ const cacheKey = `attribution:${conversionId}:${attributionModel}`;
720
+ const cached = await this.cache.get(cacheKey, 'attribution');
721
+
722
+ if (cached) {
723
+ results.push({ conversionId, attributionModel, cached, fromCache: true });
724
+ continue;
725
+ }
726
+
727
+ // Fetch user journey
728
+ const journey = await this.optimizer.getUserJourney(conversionId);
729
+
730
+ if (journey.length === 0) {
731
+ results.push({ conversionId, attributionModel, error: 'No journey found' });
732
+ continue;
733
+ }
734
+
735
+ // Calculate attribution
736
+ const attribution = await this.calculateAttribution(journey, attributionModel);
737
+
738
+ // Cache result
739
+ await this.cache.set(cacheKey, attribution, 'attribution');
740
+
741
+ results.push({ conversionId, attributionModel, attribution, fromCache: false });
742
+ } catch (error) {
743
+ console.error(`Error processing ${conversionId}:`, error);
744
+ results.push({ conversionId, attributionModel, error: error.message });
745
+ }
746
+ }
747
+
748
+ return results;
749
+ }
750
+
751
+ /**
752
+ * Calculate attribution using specified model
753
+ * @param {Array} journey - User journey events
754
+ * @param {string} model - Attribution model
755
+ * @returns {Array} Attribution result
756
+ */
757
+ async calculateAttribution(journey, model) {
758
+ // Implementation of attribution models
759
+ // This would call the appropriate attribution function from attribution-agent.md
760
+ const attributionModels = {
761
+ 'last_click': this.lastClickAttribution,
762
+ 'first_click': this.firstClickAttribution,
763
+ 'linear': this.linearAttribution,
764
+ 'time_decay': this.timeDecayAttribution,
765
+ 'u_shape': this.uShapeAttribution,
766
+ 'w_shape': this.wShapeAttribution,
767
+ 'data_driven': this.dataDrivenAttribution
768
+ };
769
+
770
+ const modelFn = attributionModels[model] || attributionModels['last_click'];
771
+ return await modelFn.call(this, journey);
772
+ }
773
+
774
+ // Attribution model implementations (from attribution-agent.md)
775
+ lastClickAttribution(touchpoints) {
776
+ return touchpoints.map((tp, index) => ({
777
+ ...tp,
778
+ credit_percentage: index === touchpoints.length - 1 ? 100 : 0,
779
+ is_last_click: index === touchpoints.length - 1
780
+ }));
781
+ }
782
+
783
+ firstClickAttribution(touchpoints) {
784
+ return touchpoints.map((tp, index) => ({
785
+ ...tp,
786
+ credit_percentage: index === 0 ? 100 : 0,
787
+ is_first_click: index === 0
788
+ }));
789
+ }
790
+
791
+ linearAttribution(touchpoints) {
792
+ const credit = 100 / touchpoints.length;
793
+ return touchpoints.map(tp => ({
794
+ ...tp,
795
+ credit_percentage: credit.toFixed(2)
796
+ }));
797
+ }
798
+
799
+ async timeDecayAttribution(touchpoints, decayFactor = 0.9) {
800
+ const now = Date.now();
801
+ const oneDayMs = 24 * 60 * 60 * 1000;
802
+
803
+ const touchpointsWithDays = touchpoints.map(tp => {
804
+ const daysSinceTouch = (now - tp.event_timestamp) / oneDayMs;
805
+ const decayScore = Math.pow(decayFactor, daysSinceTouch);
806
+ return { ...tp, days_since: daysSinceTouch, decay_score: decayScore };
807
+ });
808
+
809
+ const totalScore = touchpointsWithDays.reduce((sum, tp) => sum + tp.decay_score, 0);
810
+
811
+ return touchpointsWithDays.map(tp => ({
812
+ ...tp,
813
+ credit_percentage: ((tp.decay_score / totalScore) * 100).toFixed(2)
814
+ }));
815
+ }
816
+
817
+ uShapeAttribution(touchpoints) {
818
+ if (touchpoints.length === 0) return [];
819
+
820
+ const firstTouchWeight = 0.4;
821
+ const lastTouchWeight = 0.4;
822
+ const middleWeight = touchpoints.length > 2 ? 0.2 / (touchpoints.length - 2) : 0;
823
+
824
+ return touchpoints.map((tp, index) => {
825
+ if (index === 0) {
826
+ return { ...tp, credit_percentage: 40, role: 'first' };
827
+ } else if (index === touchpoints.length - 1) {
828
+ return { ...tp, credit_percentage: 40, role: 'last' };
829
+ } else {
830
+ return { ...tp, credit_percentage: middleWeight.toFixed(2), role: 'middle' };
831
+ }
832
+ });
833
+ }
834
+
835
+ wShapeAttribution(touchpoints) {
836
+ if (touchpoints.length === 0) return [];
837
+
838
+ const firstTouchWeight = 0.3;
839
+ const lastTouchWeight = 0.3;
840
+ const middleWeights = touchpoints.length > 2 ? 0.4 / 2 : 0; // 2 assist points
841
+
842
+ return touchpoints.map((tp, index) => {
843
+ const pos = index + 1;
844
+ const total = touchpoints.length;
845
+
846
+ if (pos === 1) {
847
+ return { ...tp, credit_percentage: 30, role: 'first' };
848
+ } else if (pos === total) {
849
+ return { ...tp, credit_percentage: 30, role: 'last' };
850
+ } else if (pos === Math.floor(total / 2)) {
851
+ return { ...tp, credit_percentage: 20, role: 'middle_assist_1' };
852
+ } else {
853
+ return { ...tp, credit_percentage: middleWeights.toFixed(2), role: 'middle_assist_2' };
854
+ }
855
+ });
856
+ }
857
+
858
+ async dataDrivenAttribution(touchpoints, userJourneyHistory) {
859
+ // Simplified data-driven attribution
860
+ // In production, this would analyze historical conversion data
861
+
862
+ const channelWeights = await this.calculateChannelWeights(touchpoints, userJourneyHistory);
863
+ const positionWeights = await this.calculatePositionWeights(touchpoints, userJourneyHistory);
864
+
865
+ return touchpoints.map((tp, index) => {
866
+ const baseScore = 100 / touchpoints.length;
867
+ const channelWeight = channelWeights[tp.utm_source] || 1;
868
+ const positionWeight = positionWeights[index] || 1;
869
+
870
+ const score = baseScore * channelWeight * positionWeight;
871
+
872
+ return {
873
+ ...tp,
874
+ credit_percentage: score.toFixed(2),
875
+ channel_weight: channelWeight,
876
+ position_weight: positionWeight
877
+ };
878
+ });
879
+ }
880
+
881
+ async calculateChannelWeights(touchpoints, history) {
882
+ // Simplified - in production, analyze historical data
883
+ const channels = {};
884
+ touchpoints.forEach(tp => {
885
+ channels[tp.utm_source] = (channels[tp.utm_source] || 0) + 1;
886
+ });
887
+
888
+ const total = Object.values(channels).reduce((a, b) => a + b, 0);
889
+ const weights = {};
890
+ Object.entries(channels).forEach(([channel, count]) => {
891
+ weights[channel] = (count / total) * touchpoints.length;
892
+ });
893
+
894
+ return weights;
895
+ }
896
+
897
+ async calculatePositionWeights(touchpoints, history) {
898
+ // Simplified - in production, analyze historical position conversion rates
899
+ const weights = {};
900
+ for (let i = 0; i < touchpoints.length; i++) {
901
+ weights[i] = 1;
902
+ }
903
+ return weights;
904
+ }
905
+
906
+ /**
907
+ * Chunk array into smaller arrays
908
+ * @param {Array} array - Array to chunk
909
+ * @param {number} size - Chunk size
910
+ * @returns {Array} Array of chunks
911
+ */
912
+ chunkArray(array, size) {
913
+ const chunks = [];
914
+ for (let i = 0; i < array.length; i += size) {
915
+ chunks.push(array.slice(i, i + size));
916
+ }
917
+ return chunks;
918
+ }
919
+ }
920
+
921
+ // Global batch processor instance
922
+ let batchProcessor = null;
923
+ ```
924
+
925
+ ---
926
+
927
+ ## 📊 Profileamento de Latência
928
+
929
+ ### Performance Monitoring
930
+
931
+ ```javascript
932
+ /**
933
+ * Latency Profiler
934
+ * Monitors and tracks performance metrics
935
+ */
936
+ class LatencyProfiler {
937
+ constructor() {
938
+ this.metrics = {
939
+ requests: [],
940
+ queries: [],
941
+ cacheOps: [],
942
+ externalAPIs: []
943
+ };
944
+
945
+ this.thresholds = {
946
+ request: { warning: 100, error: 200 },
947
+ query: { warning: 50, error: 100 },
948
+ cache: { warning: 10, error: 20 },
949
+ external: { warning: 500, error: 2000 }
950
+ };
951
+ }
952
+
953
+ /**
954
+ * Start profiling a request
955
+ * @param {string} requestId - Request ID
956
+ * @param {string} endpoint - Endpoint being accessed
957
+ */
958
+ startRequest(requestId, endpoint) {
959
+ this.metrics.requests.push({
960
+ id: requestId,
961
+ endpoint: endpoint,
962
+ startTime: performance.now(),
963
+ stages: []
964
+ });
965
+ }
966
+
967
+ /**
968
+ * End profiling a request
969
+ * @param {string} requestId - Request ID
970
+ * @param {object} metadata - Additional metadata
971
+ */
972
+ endRequest(requestId, metadata = {}) {
973
+ const request = this.metrics.requests.find(r => r.id === requestId);
974
+ if (!request) return;
975
+
976
+ request.endTime = performance.now();
977
+ request.duration = request.endTime - request.startTime;
978
+ request.metadata = metadata;
979
+
980
+ // Log if over threshold
981
+ if (request.duration > this.thresholds.request.error) {
982
+ console.error(`[Latency] CRITICAL: ${request.duration.toFixed(2)}ms - ${request.endpoint}`);
983
+ } else if (request.duration > this.thresholds.request.warning) {
984
+ console.warn(`[Latency] WARNING: ${request.duration.toFixed(2)}ms - ${request.endpoint}`);
985
+ }
986
+ }
987
+
988
+ /**
989
+ * Profile a D1 query
990
+ * @param {string} requestId - Request ID
991
+ * @param {string} query - SQL query
992
+ * @returns {Function} Function to call when query completes
993
+ */
994
+ profileQuery(requestId, query) {
995
+ const startTime = performance.now();
996
+
997
+ return () => {
998
+ const duration = performance.now() - startTime;
999
+
1000
+ this.metrics.queries.push({
1001
+ requestId: requestId,
1002
+ query: query.substring(0, 100),
1003
+ duration: duration,
1004
+ timestamp: Date.now()
1005
+ });
1006
+
1007
+ if (duration > this.thresholds.query.error) {
1008
+ console.error(`[Query Latency] CRITICAL: ${duration.toFixed(2)}ms - ${query.substring(0, 50)}...`);
1009
+ } else if (duration > this.thresholds.query.warning) {
1010
+ console.warn(`[Query Latency] WARNING: ${duration.toFixed(2)}ms - ${query.substring(0, 50)}...`);
1011
+ }
1012
+ };
1013
+ }
1014
+
1015
+ /**
1016
+ * Profile a cache operation
1017
+ * @param {string} requestId - Request ID
1018
+ * @param {string} operation - Cache operation (get, set, delete)
1019
+ * @param {string} layer - Cache layer (L1, L2)
1020
+ * @returns {Function} Function to call when operation completes
1021
+ */
1022
+ profileCache(requestId, operation, layer) {
1023
+ const startTime = performance.now();
1024
+
1025
+ return () => {
1026
+ const duration = performance.now() - startTime;
1027
+
1028
+ this.metrics.cacheOps.push({
1029
+ requestId: requestId,
1030
+ operation: operation,
1031
+ layer: layer,
1032
+ duration: duration,
1033
+ timestamp: Date.now()
1034
+ });
1035
+
1036
+ if (duration > this.thresholds.cache.error) {
1037
+ console.error(`[Cache Latency] CRITICAL: ${layer} ${operation} - ${duration.toFixed(2)}ms`);
1038
+ }
1039
+ };
1040
+ }
1041
+
1042
+ /**
1043
+ * Profile an external API call
1044
+ * @param {string} requestId - Request ID
1045
+ * @param {string} api - API name
1046
+ * @returns {Function} Function to call when API call completes
1047
+ */
1048
+ profileExternalAPI(requestId, api) {
1049
+ const startTime = performance.now();
1050
+
1051
+ return () => {
1052
+ const duration = performance.now() - startTime;
1053
+
1054
+ this.metrics.externalAPIs.push({
1055
+ requestId: requestId,
1056
+ api: api,
1057
+ duration: duration,
1058
+ timestamp: Date.now()
1059
+ });
1060
+
1061
+ if (duration > this.thresholds.external.error) {
1062
+ console.error(`[External API Latency] CRITICAL: ${api} - ${duration.toFixed(2)}ms`);
1063
+ } else if (duration > this.thresholds.external.warning) {
1064
+ console.warn(`[External API Latency] WARNING: ${api} - ${duration.toFixed(2)}ms`);
1065
+ }
1066
+ };
1067
+ }
1068
+
1069
+ /**
1070
+ * Get performance summary
1071
+ * @returns {object} Performance summary
1072
+ */
1073
+ getSummary() {
1074
+ const summary = {};
1075
+
1076
+ // Request metrics
1077
+ const completedRequests = this.metrics.requests.filter(r => r.duration);
1078
+ summary.requests = {
1079
+ total: completedRequests.length,
1080
+ avg: this.average(completedRequests.map(r => r.duration)),
1081
+ p50: this.percentile(completedRequests.map(r => r.duration), 50),
1082
+ p95: this.percentile(completedRequests.map(r => r.duration), 95),
1083
+ p99: this.percentile(completedRequests.map(r => r.duration), 99),
1084
+ max: Math.max(...completedRequests.map(r => r.duration), 0)
1085
+ };
1086
+
1087
+ // Query metrics
1088
+ summary.queries = {
1089
+ total: this.metrics.queries.length,
1090
+ avg: this.average(this.metrics.queries.map(q => q.duration)),
1091
+ p95: this.percentile(this.metrics.queries.map(q => q.duration), 95),
1092
+ max: Math.max(...this.metrics.queries.map(q => q.duration), 0)
1093
+ };
1094
+
1095
+ // Cache metrics
1096
+ const l1Ops = this.metrics.cacheOps.filter(o => o.layer === 'L1');
1097
+ const l2Ops = this.metrics.cacheOps.filter(o => o.layer === 'L2');
1098
+
1099
+ summary.cache = {
1100
+ l1: {
1101
+ total: l1Ops.length,
1102
+ avg: this.average(l1Ops.map(o => o.duration)),
1103
+ hitRate: this.calculateHitRate(l1Ops)
1104
+ },
1105
+ l2: {
1106
+ total: l2Ops.length,
1107
+ avg: this.average(l2Ops.map(o => o.duration)),
1108
+ hitRate: this.calculateHitRate(l2Ops)
1109
+ }
1110
+ };
1111
+
1112
+ // External API metrics
1113
+ summary.external = {
1114
+ total: this.metrics.externalAPIs.length,
1115
+ avg: this.average(this.metrics.externalAPIs.map(a => a.duration)),
1116
+ p95: this.percentile(this.metrics.externalAPIs.map(a => a.duration), 95),
1117
+ byAPI: this.groupByAPI()
1118
+ };
1119
+
1120
+ return summary;
1121
+ }
1122
+
1123
+ /**
1124
+ * Calculate average of array
1125
+ * @param {number[]} arr - Array of numbers
1126
+ * @returns {number} Average
1127
+ */
1128
+ average(arr) {
1129
+ if (arr.length === 0) return 0;
1130
+ return arr.reduce((a, b) => a + b, 0) / arr.length;
1131
+ }
1132
+
1133
+ /**
1134
+ * Calculate percentile of array
1135
+ * @param {number[]} arr - Array of numbers
1136
+ * @param {number} p - Percentile (0-100)
1137
+ * @returns {number} Percentile value
1138
+ */
1139
+ percentile(arr, p) {
1140
+ if (arr.length === 0) return 0;
1141
+ const sorted = arr.sort((a, b) => a - b);
1142
+ const index = Math.ceil((p / 100) * sorted.length) - 1;
1143
+ return sorted[index];
1144
+ }
1145
+
1146
+ /**
1147
+ * Calculate cache hit rate
1148
+ * @param {Array} ops - Cache operations
1149
+ * @returns {number} Hit rate percentage
1150
+ */
1151
+ calculateHitRate(ops) {
1152
+ const hits = ops.filter(o => o.operation === 'get').length;
1153
+ const total = ops.length;
1154
+ return total > 0 ? ((hits / total) * 100).toFixed(2) + '%' : '0%';
1155
+ }
1156
+
1157
+ /**
1158
+ * Group external API calls by API
1159
+ * @returns {object} Grouped metrics
1160
+ */
1161
+ groupByAPI() {
1162
+ const grouped = {};
1163
+ this.metrics.externalAPIs.forEach(call => {
1164
+ if (!grouped[call.api]) {
1165
+ grouped[call.api] = { total: 0, durations: [] };
1166
+ }
1167
+ grouped[call.api].total++;
1168
+ grouped[call.api].durations.push(call.duration);
1169
+ });
1170
+
1171
+ Object.keys(grouped).forEach(api => {
1172
+ grouped[api].avg = this.average(grouped[api].durations);
1173
+ grouped[api].p95 = this.percentile(grouped[api].durations, 95);
1174
+ });
1175
+
1176
+ return grouped;
1177
+ }
1178
+
1179
+ /**
1180
+ * Clear all metrics
1181
+ */
1182
+ clear() {
1183
+ this.metrics = {
1184
+ requests: [],
1185
+ queries: [],
1186
+ cacheOps: [],
1187
+ externalAPIs: []
1188
+ };
1189
+ }
1190
+ }
1191
+
1192
+ // Global latency profiler instance
1193
+ let latencyProfiler = new LatencyProfiler();
1194
+ ```
1195
+
1196
+ ---
1197
+
1198
+ ## 🔧 Endpoints de Monitoramento de Performance
1199
+
1200
+ ### Worker API Endpoints
1201
+
1202
+ ```javascript
1203
+ /**
1204
+ * Performance Monitoring Endpoints
1205
+ */
1206
+ export default {
1207
+ async fetch(request, env, ctx) {
1208
+ // Initialize components
1209
+ if (!cacheManager) cacheManager = new CacheManager(env);
1210
+ if (!queryOptimizer) queryOptimizer = new QueryOptimizer(env.DB);
1211
+ if (!batchProcessor) batchProcessor = new BatchAttributionProcessor(cacheManager, queryOptimizer);
1212
+
1213
+ const url = new URL(request.url);
1214
+ const path = url.pathname;
1215
+
1216
+ // Performance monitoring endpoints
1217
+ if (path === '/api/performance/stats') {
1218
+ return handlePerformanceStats(env);
1219
+ }
1220
+
1221
+ if (path === '/api/performance/cache-stats') {
1222
+ return handleCacheStats(env);
1223
+ }
1224
+
1225
+ if (path === '/api/performance/query-stats') {
1226
+ return handleQueryStats(env);
1227
+ }
1228
+
1229
+ if (path === '/api/performance/latency-summary') {
1230
+ return handleLatencySummary(env);
1231
+ }
1232
+
1233
+ if (path === '/api/performance/cache-invalidate') {
1234
+ return handleCacheInvalidate(request, env);
1235
+ }
1236
+
1237
+ // ... other endpoints
1238
+
1239
+ return new Response('Not Found', { status: 404 });
1240
+ }
1241
+ };
1242
+
1243
+ /**
1244
+ * Handle /api/performance/stats - Overall performance statistics
1245
+ */
1246
+ async function handlePerformanceStats(env) {
1247
+ const summary = latencyProfiler.getSummary();
1248
+
1249
+ return new Response(JSON.stringify({
1250
+ success: true,
1251
+ data: {
1252
+ performance: summary,
1253
+ timestamp: new Date().toISOString()
1254
+ }
1255
+ }), {
1256
+ headers: { 'Content-Type': 'application/json' }
1257
+ });
1258
+ }
1259
+
1260
+ /**
1261
+ * Handle /api/performance/cache-stats - Cache statistics
1262
+ */
1263
+ async function handleCacheStats(env) {
1264
+ const stats = cacheManager ? cacheManager.getStats() : null;
1265
+
1266
+ return new Response(JSON.stringify({
1267
+ success: true,
1268
+ data: {
1269
+ cache: stats,
1270
+ timestamp: new Date().toISOString()
1271
+ }
1272
+ }), {
1273
+ headers: { 'Content-Type': 'application/json' }
1274
+ });
1275
+ }
1276
+
1277
+ /**
1278
+ * Handle /api/performance/query-stats - Query statistics
1279
+ */
1280
+ async function handleQueryStats(env) {
1281
+ const queryMetrics = latencyProfiler.metrics.queries;
1282
+
1283
+ return new Response(JSON.stringify({
1284
+ success: true,
1285
+ data: {
1286
+ queries: {
1287
+ total: queryMetrics.length,
1288
+ avg: queryMetrics.length > 0
1289
+ ? queryMetrics.reduce((a, b) => a + b.duration, 0) / queryMetrics.length
1290
+ : 0,
1291
+ slowQueries: queryMetrics.filter(q => q.duration > 100),
1292
+ timestamp: new Date().toISOString()
1293
+ }
1294
+ }
1295
+ }), {
1296
+ headers: { 'Content-Type': 'application/json' }
1297
+ });
1298
+ }
1299
+
1300
+ /**
1301
+ * Handle /api/performance/latency-summary - Latency summary
1302
+ */
1303
+ async function handleLatencySummary(env) {
1304
+ const summary = latencyProfiler.getSummary();
1305
+
1306
+ return new Response(JSON.stringify({
1307
+ success: true,
1308
+ data: {
1309
+ latency: {
1310
+ requests: summary.requests,
1311
+ queries: summary.queries,
1312
+ cache: summary.cache,
1313
+ externalAPIs: summary.external
1314
+ },
1315
+ timestamp: new Date().toISOString()
1316
+ }
1317
+ }), {
1318
+ headers: { 'Content-Type': 'application/json' }
1319
+ });
1320
+ }
1321
+
1322
+ /**
1323
+ * Handle /api/performance/cache-invalidate - Cache invalidation
1324
+ */
1325
+ async function handleCacheInvalidate(request, env) {
1326
+ if (request.method !== 'POST') {
1327
+ return new Response('Method Not Allowed', { status: 405 });
1328
+ }
1329
+
1330
+ try {
1331
+ const body = await request.json();
1332
+ const { prefix, keys } = body;
1333
+
1334
+ if (prefix) {
1335
+ await cacheManager.invalidateByPrefix(prefix);
1336
+ } else if (keys && Array.isArray(keys)) {
1337
+ for (const key of keys) {
1338
+ await cacheManager.delete(key);
1339
+ }
1340
+ } else {
1341
+ return new Response(JSON.stringify({
1342
+ success: false,
1343
+ error: 'Must provide either prefix or keys'
1344
+ }), {
1345
+ status: 400,
1346
+ headers: { 'Content-Type': 'application/json' }
1347
+ });
1348
+ }
1349
+
1350
+ return new Response(JSON.stringify({
1351
+ success: true,
1352
+ message: 'Cache invalidated successfully',
1353
+ timestamp: new Date().toISOString()
1354
+ }), {
1355
+ headers: { 'Content-Type': 'application/json' }
1356
+ });
1357
+ } catch (error) {
1358
+ return new Response(JSON.stringify({
1359
+ success: false,
1360
+ error: error.message
1361
+ }), {
1362
+ status: 500,
1363
+ headers: { 'Content-Type': 'application/json' }
1364
+ });
1365
+ }
1366
+ }
1367
+ ```
1368
+
1369
+ ---
1370
+
1371
+ ## 📋 Resumo de Implementação
1372
+
1373
+ ### Componentes Criados
1374
+
1375
+ 1. **CacheManager** - Gerenciador de caching multi-camada (L1 + L2 + L3)
1376
+ 2. **QueryOptimizer** - Otimizador de queries com cache e índices
1377
+ 3. **BatchAttributionProcessor** - Processamento em lote de atribuições
1378
+ 4. **LatencyProfiler** - Monitoramento de latência em tempo real
1379
+
1380
+ ### Endpoints de Monitoramento
1381
+
1382
+ | Endpoint | Método | Descrição |
1383
+ |----------|--------|-----------|
1384
+ | `/api/performance/stats` | GET | Estatísticas gerais de performance |
1385
+ | `/api/performance/cache-stats` | GET | Estatísticas de cache (L1/L2) |
1386
+ | `/api/performance/query-stats` | GET | Estatísticas de queries |
1387
+ | `/api/performance/latency-summary` | GET | Resumo de latência (P50, P95, P99) |
1388
+ | `/api/performance/cache-invalidate` | POST | Invalidação de cache |
1389
+
1390
+ ### Melhorias de Performance
1391
+
1392
+ - **Cache Hit Rate**: > 95% (L1 + L2)
1393
+ - **P95 Latency**: < 100ms (tracking), < 500ms (attribution)
1394
+ - **Query Time**: < 50ms (queries otimizadas)
1395
+ - **Memory Usage**: < 128MB (workers)
1396
+ - **CPU Time**: < 50ms (workers)
1397
+
1398
+ ---
1399
+
1400
+ ## 🔗 Integração com Outros Agentes
1401
+
1402
+ - **attribution-agent.md**: Usa `BatchAttributionProcessor` para processar múltiplas atribuições em paralelo
1403
+ - **security-enterprise-agent.md**: Usa `CacheManager` para cache de configurações de segurança
1404
+ - **master-orchestrator.md**: Usa `LatencyProfiler` para monitorar performance de toda a pipeline
1405
+
1406
+ ---
1407
+
1408
+ ## ✅ Checklist de Implementação
1409
+
1410
+ - [x] Implementar L1 Memory Cache (request-scoped)
1411
+ - [x] Implementar L2 KV Cache (global edge cache)
1412
+ - [x] Implementar CacheManager com fallback L1 → L2 → D1
1413
+ - [x] Criar índices otimizados para D1
1414
+ - [x] Implementar QueryOptimizer com cache de queries
1415
+ - [x] Implementar BatchAttributionProcessor para processamento em lote
1416
+ - [x] Implementar LatencyProfiler com monitoramento em tempo real
1417
+ - [x] Criar endpoints de monitoramento de performance
1418
+ - [x] Implementar invalidação de cache por prefixo
1419
+ - [x] Configurar TTLs específicos por tipo de cache
1420
+
1421
+ ---
1422
+
1423
+ ## 📚 Referências
1424
+
1425
+ - [Cloudflare Workers Performance Best Practices](https://developers.cloudflare.com/workers/)
1426
+ - [Cloudflare KV Caching](https://developers.cloudflare.com/workers/runtime-apis/kv/)
1427
+ - [Cloudflare D1 Query Optimization](https://developers.cloudflare.com/d1/)
1428
+ - [SQLite Index Optimization](https://www.sqlite.org/queryplanner.html)
1429
+
1430
+ ---
1431
+
1432
+ *Performance Optimization Enterprise Agent v1.0.0 - CDP Edge Quantum Tier*