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 {}