mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 11:06:49 +00:00
liftweb and http4s together
This commit is contained in:
parent
9260ac4e0a
commit
7dd62a89b4
@ -252,6 +252,185 @@ Phase 4 (Complete):
|
||||
|
||||
**This way http4s is NEVER dependent on Jetty.**
|
||||
|
||||
### Is http4s Non-Blocking?
|
||||
|
||||
**YES - http4s is fully non-blocking and asynchronous.**
|
||||
|
||||
#### Architecture Comparison
|
||||
|
||||
**Lift/Jetty (Blocking):**
|
||||
```
|
||||
Thread-per-request model:
|
||||
┌─────────────────────────────┐
|
||||
│ Request 1 → Thread 1 (busy) │ Blocks waiting for DB
|
||||
│ Request 2 → Thread 2 (busy) │ Blocks waiting for HTTP call
|
||||
│ Request 3 → Thread 3 (busy) │ Blocks waiting for file I/O
|
||||
│ Request 4 → Thread 4 (busy) │
|
||||
│ ... │
|
||||
│ Request N → Thread pool full │ ← New requests wait!
|
||||
└─────────────────────────────┘
|
||||
|
||||
Problem:
|
||||
- 1 thread per request
|
||||
- Thread blocks on I/O
|
||||
- Limited by thread pool size (e.g., 200 threads)
|
||||
- More requests = more memory
|
||||
```
|
||||
|
||||
**http4s (Non-Blocking):**
|
||||
```
|
||||
Async/Effect model:
|
||||
┌─────────────────────────────┐
|
||||
│ Thread 1: │
|
||||
│ Request 1 → DB call (IO) │ ← Doesn't block! Fiber suspended
|
||||
│ Request 2 → API call (IO) │ ← Continues processing
|
||||
│ Request 3 → Processing │
|
||||
│ Request N → ... │
|
||||
└─────────────────────────────┘
|
||||
|
||||
Benefits:
|
||||
- Few threads (typically = CPU cores)
|
||||
- Thousands of concurrent requests
|
||||
- Much lower memory usage
|
||||
- Scales better
|
||||
```
|
||||
|
||||
#### Performance Impact
|
||||
|
||||
**Lift/Jetty:**
|
||||
- 200 threads × ~1MB stack = ~200MB just for threads
|
||||
- Max ~200 concurrent blocking requests
|
||||
- Each blocked thread = wasted resources
|
||||
|
||||
**http4s:**
|
||||
- 8 threads (on 8-core machine) × ~1MB = ~8MB for threads
|
||||
- Can handle 10,000+ concurrent requests
|
||||
- Threads never block, always doing work
|
||||
|
||||
#### Code Example - Blocking vs Non-Blocking
|
||||
|
||||
**Lift (Blocking):**
|
||||
```scala
|
||||
// This BLOCKS the thread while waiting for DB
|
||||
lazy val getBank: OBPEndpoint = {
|
||||
case "banks" :: bankId :: Nil JsonGet _ => {
|
||||
cc => implicit val ec = EndpointContext(Some(cc))
|
||||
for {
|
||||
// Thread blocks here waiting for database
|
||||
bank <- Future { Connector.connector.vend.getBank(BankId(bankId)) }
|
||||
} yield {
|
||||
(bank, HttpCode.`200`(cc))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Under the hood:
|
||||
// Thread 1: Wait for DB... (blocked, not doing anything else)
|
||||
// Thread 2: Wait for DB... (blocked)
|
||||
// Thread 3: Wait for DB... (blocked)
|
||||
// Eventually: No threads left → requests queue up
|
||||
```
|
||||
|
||||
**http4s (Non-Blocking):**
|
||||
```scala
|
||||
// This NEVER blocks - thread is freed while waiting
|
||||
def getBank[F[_]: Concurrent](bankId: String): F[Response[F]] = {
|
||||
for {
|
||||
// Thread is released while waiting for DB
|
||||
// Can handle other requests in the meantime
|
||||
bank <- getBankFromDB(bankId) // Returns F[Bank], doesn't block
|
||||
response <- Ok(bank.asJson)
|
||||
} yield response
|
||||
}
|
||||
|
||||
// Under the hood:
|
||||
// Thread 1: Start DB call → release thread → handle other requests
|
||||
// DB returns → pick up continuation → send response
|
||||
// Same thread handles 100s of requests while others wait for I/O
|
||||
```
|
||||
|
||||
#### Real-World Impact
|
||||
|
||||
**Scenario:** 1000 concurrent requests, each needs 100ms of DB time
|
||||
|
||||
**Lift/Jetty (200 thread pool):**
|
||||
- First 200 requests: start immediately
|
||||
- Requests 201-1000: wait in queue
|
||||
- Total time: ~500ms (because of queuing)
|
||||
- Memory: 200MB for threads
|
||||
|
||||
**http4s (8 threads):**
|
||||
- All 1000 requests: start immediately
|
||||
- Process concurrently on 8 threads
|
||||
- Total time: ~100ms (no queuing)
|
||||
- Memory: 8MB for threads
|
||||
|
||||
#### Why This Matters for Migration
|
||||
|
||||
1. **Better Resource Usage**
|
||||
- Same machine can handle more requests
|
||||
- Lower memory footprint
|
||||
- Can scale vertically better
|
||||
|
||||
2. **No Thread Pool Tuning**
|
||||
- Lift: Need to tune thread pool size (too small = slow, too large = OOM)
|
||||
- http4s: Set to CPU cores, done
|
||||
|
||||
3. **Database Connections**
|
||||
- Lift: Need thread pool ≤ DB connections (e.g., 200 threads = 200 DB connections)
|
||||
- http4s: 8 threads can share smaller DB pool (e.g., 20 connections)
|
||||
|
||||
4. **Modern Architecture**
|
||||
- http4s uses cats-effect (like Akka, ZIO, Monix)
|
||||
- Industry standard for Scala backends
|
||||
- Better ecosystem and tooling
|
||||
|
||||
#### The Blocking Problem in Current OBP-API
|
||||
|
||||
```scala
|
||||
// Common pattern in OBP-API - BLOCKS thread
|
||||
for {
|
||||
bank <- Future { /* Get from DB - BLOCKS */ }
|
||||
accounts <- Future { /* Get from DB - BLOCKS */ }
|
||||
transactions <- Future { /* Get from Connector - BLOCKS */ }
|
||||
} yield result
|
||||
|
||||
// Each Future ties up a thread waiting
|
||||
// If 200 requests do this, 200 threads blocked
|
||||
```
|
||||
|
||||
#### http4s Solution - Truly Async
|
||||
|
||||
```scala
|
||||
// Non-blocking version
|
||||
for {
|
||||
bank <- IO { /* Get from DB - thread released */ }
|
||||
accounts <- IO { /* Get from DB - thread released */ }
|
||||
transactions <- IO { /* Get from Connector - thread released */ }
|
||||
} yield result
|
||||
|
||||
// IO suspends computation, releases thread
|
||||
// Thread can handle other work while waiting
|
||||
// 8 threads can handle 1000s of these concurrently
|
||||
```
|
||||
|
||||
### Conclusion on Non-Blocking
|
||||
|
||||
**Yes, http4s is non-blocking and this is a MAJOR reason to migrate:**
|
||||
|
||||
- Better performance (10-50x more concurrent requests)
|
||||
- Lower memory usage
|
||||
- Better resource utilization
|
||||
- Scales much better
|
||||
- Removes need for thread pool tuning
|
||||
|
||||
**However:** To get full benefits, you'll need to:
|
||||
1. Use `IO` or `F[_]` instead of blocking `Future`
|
||||
2. Use non-blocking database libraries (Doobie, Skunk)
|
||||
3. Use non-blocking HTTP clients (http4s client)
|
||||
|
||||
But the migration can be gradual - even blocking code in http4s is still better than Lift/Jetty's servlet model.
|
||||
|
||||
### Step 4: Shared Business Logic
|
||||
|
||||
```scala
|
||||
|
||||
Loading…
Reference in New Issue
Block a user