# Penanganan Concurrent Requests dengan Data Berbeda

## Skenario yang Dimaksud

**2 request HTTP bersamaan dengan data yang berbeda** (UUID berbeda, payload berbeda):

**Request 1 (Goroutine 1):**
```
POST /webhook/accurate
Body: {"type": "SALES_RECEIPT", "uuid": "abc-123", "data": [{"salesReceiptNo": "BKB-001"}]}
```

**Request 2 (Goroutine 2, bersamaan):**
```
POST /webhook/accurate
Body: {"type": "SALES_RECEIPT", "uuid": "def-456", "data": [{"salesReceiptNo": "BKB-002"}]}
```

**Keduanya datang bersamaan, data berbeda, tidak duplicate.**

---

## Apakah Sudah Di-Handle?

### ✅ Ya, Sudah Aman!

**Alasan:**

1. **Go's HTTP Server - Thread-Safe by Default**
   - Setiap request HTTP di-handle di **goroutine terpisah**
   - Tidak ada shared state antar request
   - Handler bisa dipanggil bersamaan tanpa race condition

2. **SQLite dengan WAL Mode - Concurrent-Safe**
   ```go
   PRAGMA journal_mode = WAL;        // Write-Ahead Logging
   PRAGMA busy_timeout = 5000;      // Retry 5 detik jika locked
   ```
   - **Concurrent reads**: Multiple goroutine bisa baca bersamaan ✅
   - **Concurrent writes**: Jika 2 goroutine INSERT bersamaan:
     - SQLite akan lock database untuk write pertama
     - Write kedua akan **wait** (karena `busy_timeout = 5000`)
     - Setelah write pertama selesai, write kedua lanjut otomatis ✅
   - Tidak ada data corruption atau lost data

3. **Handler - No Shared State**
   - Setiap request punya variabel lokal sendiri (`queueIDs`, `payloads`, dll)
   - Tidak ada global variable yang di-modify
   - INSERT ke database adalah operasi atomic

4. **Worker Pool - Multiple Workers**
   - Default **5 workers** (bisa diubah via `WORKER_COUNT`)
   - Setiap worker process job secara **independent**
   - Channel buffer 1000 untuk handle burst

---

## Alur Concurrent Requests

### Timeline: 2 Request Bersamaan dengan Data Berbeda

**T=0ms:**
```
Goroutine 1: Request 1 masuk → Parse payload UUID="abc-123"
Goroutine 2: Request 2 masuk → Parse payload UUID="def-456"
```

**T=1ms:**
```
Goroutine 1: Check UUID "abc-123" → Tidak ada → Lanjut INSERT
Goroutine 2: Check UUID "def-456" → Tidak ada → Lanjut INSERT
```

**T=2ms:**
```
Goroutine 1: INSERT ke webhook_queue (UUID="abc-123") → Success → queue_id=123
Goroutine 2: INSERT ke webhook_queue (UUID="def-456") → Wait (DB locked) → Retry...
```

**T=3ms:**
```
Goroutine 1: Submit queue_id=123 ke worker pool → Done
Goroutine 2: INSERT berhasil → queue_id=124 → Submit ke worker pool → Done
```

**T=4ms:**
```
Worker 1: Process queue_id=123 (UUID="abc-123")
Worker 2: Process queue_id=124 (UUID="def-456")
```

**Hasil:** ✅ **Keduanya masuk queue dan diproses dengan benar!**

---

## Test Concurrent Requests

**Test dengan 2 request bersamaan:**

```bash
# Terminal 1
curl -X POST http://202.155.95.172:9000/webhook/accurate \
  -H "Content-Type: application/json" \
  -d '{"type":"SALES_RECEIPT","uuid":"test-concurrent-1","databaseId":1293289,"data":[{"salesReceiptNo":"BKB-001"}]}' &

# Terminal 2 (bersamaan)
curl -X POST http://202.155.95.172:9000/webhook/accurate \
  -H "Content-Type: application/json" \
  -d '{"type":"SALES_RECEIPT","uuid":"test-concurrent-2","databaseId":1293289,"data":[{"salesReceiptNo":"BKB-002"}]}' &

wait
```

**Expected log:**
```
📥 [Webhook] Incoming POST /webhook/accurate Content-Length:208
📥 [Webhook] Incoming POST /webhook/accurate Content-Length:208
  → [Webhook] Queued payload 1/1: type=SALES_RECEIPT, uuid=test-concurrent-1, queue_id=123
  → [Webhook] Queued payload 1/1: type=SALES_RECEIPT, uuid=test-concurrent-2, queue_id=124
✅ [Webhook] Successfully queued 1 payload(s) - queue IDs: [123]
✅ [Webhook] Successfully queued 1 payload(s) - queue IDs: [124]
```

**Verifikasi di SQLite:**
```bash
sqlite3 /opt/webhook-service/data/webhook.db "
SELECT id, uuid, type, datetime(created_at) 
FROM webhook_queue 
WHERE uuid IN ('test-concurrent-1', 'test-concurrent-2')
ORDER BY id;
"
```

**Expected:** 2 rows dengan UUID berbeda, keduanya masuk.

---

## Potensi Masalah & Solusi

### ❓ Apakah Ada Race Condition?

**Tidak ada race condition** karena:

1. **UUID Check**: `SELECT ... WHERE uuid = ?` adalah read operation, bisa concurrent
2. **INSERT**: SQLite handle locking otomatis dengan `busy_timeout`
3. **Unique Index**: Jika ada race condition (2 request INSERT UUID sama bersamaan), unique index akan catch dan kita handle dengan double check

### ❓ Apakah Ada Deadlock?

**Tidak ada deadlock** karena:

1. Semua operasi database adalah **short-lived** (INSERT cepat)
2. `busy_timeout = 5000` mencegah infinite wait
3. Tidak ada nested transaction atau lock dependency

### ❓ Apakah Ada Data Loss?

**Tidak ada data loss** karena:

1. SQLite dengan WAL mode **guarantee consistency**
2. Jika INSERT gagal karena lock, akan retry otomatis
3. Jika retry masih gagal setelah 5 detik, error di-log dan request return error (bukan silent fail)

---

## Performance dengan Concurrent Requests

**Dengan 5 workers (default):**

- **Throughput**: Bisa handle **5 concurrent processing** (satu per worker)
- **Queue Buffer**: 1000 jobs di channel (handle burst)
- **Response Time**: ~10-50ms per request (tergantung DB lock)

**Jika banyak concurrent requests:**

1. Request masuk → Parse → Check UUID → INSERT (jika perlu wait, max 5 detik)
2. Submit ke worker pool (non-blocking, channel buffer 1000)
3. Return response **segera** (tidak tunggu processing)
4. Worker process di background

**Jadi:** Sistem bisa handle **banyak concurrent requests** tanpa blocking atau data loss.

---

## Kesimpulan

✅ **Concurrent requests dengan data berbeda** → **Sudah di-handle dengan benar**

**Alasan:**
- Go's HTTP server thread-safe (goroutine per request)
- SQLite WAL mode support concurrent operations
- `busy_timeout` handle database locking gracefully
- Worker pool handle multiple jobs bersamaan
- Tidak ada shared state atau race condition

**Tidak perlu perubahan tambahan** - sistem sudah aman untuk concurrent requests! 🎉
