Section 8/101 menit

8. Real Use Cases

8. Real Use Cases

Use Case 1: Rate Limiter Thread-Safe

swift
import Synchronization
import Foundation

// Rate limiter yang bisa dipakai dari banyak thread tanpa actor overhead
final class RateLimiter: Sendable {
    struct State {
        var tokens: Double
        var lastRefill: Date
    }
    
    private let state: Mutex<State>
    private let maxTokens: Double
    private let refillRate: Double  // tokens per second
    
    init(maxTokens: Double, refillRate: Double) {
        self.maxTokens = maxTokens
        self.refillRate = refillRate
        self.state = Mutex(State(tokens: maxTokens, lastRefill: Date()))
    }
    
    // Thread-safe: boleh dipanggil dari mana saja
    func tryConsume(tokens: Double = 1) -> Bool {
        state.withLock { state in
            // Refill berdasarkan waktu yang berlalu
            let now = Date()
            let elapsed = now.timeIntervalSince(state.lastRefill)
            state.tokens = min(maxTokens, state.tokens + elapsed * refillRate)
            state.lastRefill = now
            
            // Cek dan konsumsi token
            guard state.tokens >= tokens else { return false }
            state.tokens -= tokens
            return true
        }
    }
    
    var availableTokens: Double {
        state.withLock { $0.tokens }
    }
}

// Penggunaan di networking layer:
let apiRateLimiter = RateLimiter(maxTokens: 100, refillRate: 10)

func makeAPIRequest() async throws -> Data {
    guard apiRateLimiter.tryConsume() else {
        throw APIError.rateLimitExceeded
    }
    // ... lakukan request
    return Data()
}

Use Case 2: Request Deduplication dengan Atomic

swift
import Synchronization

// Track request IDs secara lock-free untuk deduplication
final class RequestDeduplicator: Sendable {
    // Gunakan Atomic untuk counter sederhana
    private let totalRequests = Atomic(0)
    private let duplicatesBlocked = Atomic(0)
    
    // Gunakan Mutex untuk state yang lebih kompleks
    private let activeRequestIDs = Mutex(Set<String>())
    
    func shouldProcess(requestID: String) -> Bool {
        totalRequests.add(1, ordering: .relaxed)
        
        return activeRequestIDs.withLock { activeIDs in
            guard !activeIDs.contains(requestID) else {
                duplicatesBlocked.add(1, ordering: .relaxed)
                return false
            }
            activeIDs.insert(requestID)
            return true
        }
    }
    
    func complete(requestID: String) {
        activeRequestIDs.withLock { activeIDs in
            activeIDs.remove(requestID)
        }
    }
    
    var stats: (total: Int, duplicatesBlocked: Int) {
        (
            total: totalRequests.load(ordering: .relaxed),
            duplicatesBlocked: duplicatesBlocked.load(ordering: .relaxed)
        )
    }
}

Use Case 3: Metrics Collector dengan Atomic

swift
import Synchronization
import Foundation

// High-performance metrics — Atomic untuk throughput tinggi
final class PerformanceMetrics: Sendable {
    // Gunakan Atomic untuk hot-path counters (dipanggil ribuan kali/detik)
    private let _requestCount = Atomic(Int64(0))
    private let _errorCount = Atomic(Int64(0))
    private let _totalLatencyNanos = Atomic(Int64(0))
    private let _minLatencyNanos = Atomic(Int64.max)
    private let _maxLatencyNanos = Atomic(Int64(0))
    
    func record(latencyNanos: Int64, isError: Bool) {
        _requestCount.add(1, ordering: .relaxed)
        _totalLatencyNanos.add(latencyNanos, ordering: .relaxed)
        
        if isError {
            _errorCount.add(1, ordering: .relaxed)
        }
        
        // Update min/max dengan CAS loop
        var currentMin = _minLatencyNanos.load(ordering: .relaxed)
        while latencyNanos < currentMin {
            let (exchanged, newMin) = _minLatencyNanos.compareExchange(
                expected: currentMin,
                desired: latencyNanos,
                ordering: .relaxed
            )
            if exchanged { break }
            currentMin = newMin
        }
        
        var currentMax = _maxLatencyNanos.load(ordering: .relaxed)
        while latencyNanos > currentMax {
            let (exchanged, newMax) = _maxLatencyNanos.compareExchange(
                expected: currentMax,
                desired: latencyNanos,
                ordering: .relaxed
            )
            if exchanged { break }
            currentMax = newMax
        }
    }
    
    var snapshot: MetricsSnapshot {
        let count = _requestCount.load(ordering: .acquiring)
        return MetricsSnapshot(
            requestCount: count,
            errorCount: _errorCount.load(ordering: .relaxed),
            averageLatencyMs: count > 0
                ? Double(_totalLatencyNanos.load(ordering: .relaxed)) / Double(count) / 1_000_000
                : 0,
            minLatencyMs: Double(_minLatencyNanos.load(ordering: .relaxed)) / 1_000_000,
            maxLatencyMs: Double(_maxLatencyNanos.load(ordering: .relaxed)) / 1_000_000
        )
    }
}

struct MetricsSnapshot: Sendable {
    let requestCount: Int64
    let errorCount: Int64
    let averageLatencyMs: Double
    let minLatencyMs: Double
    let maxLatencyMs: Double
}

Use Case 4: Plugin Registry dengan Mutex

swift
import Synchronization

// Registry yang diisi dari berbagai thread (plugin loading), dibaca dari banyak tempat
final class PluginRegistry: Sendable {
    struct RegistryState {
        var plugins: [String: any Plugin] = [:]
        var loadOrder: [String] = []
    }
    
    private let state = Mutex(RegistryState())
    
    func register(_ plugin: any Plugin, for key: String) {
        state.withLock { registry in
            registry.plugins[key] = plugin
            registry.loadOrder.append(key)
        }
        print("Plugin registered: \(key)")
    }
    
    func plugin(for key: String) -> (any Plugin)? {
        state.withLock { registry in
            registry.plugins[key]
        }
    }
    
    func allPlugins() -> [any Plugin] {
        state.withLock { registry in
            registry.loadOrder.compactMap { registry.plugins[$0] }
        }
    }
    
    func unregister(key: String) {
        state.withLock { registry in
            registry.plugins.removeValue(forKey: key)
            registry.loadOrder.removeAll { $0 == key }
        }
    }
    
    // Non-blocking: cek apakah plugin ada tanpa menunggu
    func hasPlugin(for key: String) -> Bool {
        state.withLockIfAvailable { registry in
            registry.plugins[key] != nil
        } ?? false  // jika lock tidak tersedia, anggap tidak ada
    }
}

protocol Plugin {}