Section 8/91 menit

8. Real Use Cases

8. Real Use Cases

Use Case 1: Database Transaction Guard

swift
import Foundation

enum TransactionError: Error {
    case connectionFailed
    case constraintViolation(String)
    case deadlock
    case rollbackFailed
}

// Transaction yang PASTI di-commit atau di-rollback — tidak ada yang lolos
struct DatabaseTransaction: ~Copyable {
    private let id: UUID
    private let connection: DatabaseConnection
    private var state: State = .active
    
    enum State { case active, committed, rolledBack }
    
    init(connection: DatabaseConnection) throws(TransactionError) {
        self.id = UUID()
        self.connection = connection
        try connection.beginTransaction(id: id)
    }
    
    // consuming: setelah commit, transaction tidak bisa dipakai
    consuming func commit() throws(TransactionError) {
        guard state == .active else { return }
        state = .committed
        try connection.commitTransaction(id: id)
    }
    
    consuming func rollback() {
        guard state == .active else { return }
        state = .rolledBack
        try? connection.rollbackTransaction(id: id)
    }
    
    // borrowing: execute query tanpa mengambil ownership transaction
    borrowing func execute(_ sql: String) throws(TransactionError) {
        guard state == .active else {
            throw TransactionError.connectionFailed
        }
        try connection.execute(sql, in: id)
    }
    
    deinit {
        // Safety net: jika keluar scope tanpa commit/rollback
        if state == .active {
            try? connection.rollbackTransaction(id: id)
            print("⚠️ Transaction \(id) di-rollback otomatis karena tidak di-commit")
        }
    }
}

// Helper untuk RAII-style transaction management
func withTransaction<Result>(
    on connection: DatabaseConnection,
    body: (borrowing DatabaseTransaction) throws -> Result
) throws -> Result {
    var tx = try DatabaseTransaction(connection: connection)
    
    do {
        let result = try body(tx)
        try (consume tx).commit()
        return result
    } catch {
        (consume tx).rollback()
        throw error
    }
}

// Penggunaan — transaction PASTI di-commit atau di-rollback
func transferFunds(from sourceID: String, to destID: String, amount: Double) async throws {
    let connection = try await DatabaseConnection.connect()
    
    try withTransaction(on: connection) { tx in
        try tx.execute("UPDATE accounts SET balance = balance - \(amount) WHERE id = '\(sourceID)'")
        try tx.execute("UPDATE accounts SET balance = balance + \(amount) WHERE id = '\(destID)'")
        try tx.execute("INSERT INTO transfers VALUES ('\(sourceID)', '\(destID)', \(amount))")
        // commit terjadi otomatis saat withTransaction selesai
    }
    // Jika ada exception: rollback otomatis via deinit
}

Use Case 2: Mutex Lock Guard (RAII Pattern)

swift
// RAII: Resource Acquisition Is Initialization
// Lock diperoleh saat init, dilepas saat deinit — tidak bisa bocor

struct MutexGuard<Value>: ~Copyable {
    private let mutex: NSLock
    private let valuePtr: UnsafeMutablePointer<Value>
    
    fileprivate init(mutex: NSLock, valuePtr: UnsafeMutablePointer<Value>) {
        self.mutex = mutex
        self.valuePtr = valuePtr
        mutex.lock()  // acquire saat init
    }
    
    // Akses nilai yang dilindungi
    var value: Value {
        get { valuePtr.pointee }
        set { valuePtr.pointee = newValue }
    }
    
    deinit {
        mutex.unlock()  // release PASTI terjadi — tidak ada lock leak
    }
}

final class Protected<Value> {
    private let mutex = NSLock()
    private var _value: Value
    
    init(_ value: Value) {
        self._value = value
    }
    
    func withLock<Result>(_ body: (inout Value) throws -> Result) rethrows -> Result {
        mutex.lock()
        defer { mutex.unlock() }
        return try body(&_value)
    }
    
    // Menggunakan MutexGuard untuk ownership yang jelas
    func lock() -> MutexGuard<Value> {
        MutexGuard(mutex: mutex, valuePtr: &_value)
    }
}

// Penggunaan:
let counter = Protected(0)

// Cara 1: withLock closure
counter.withLock { value in
    value += 1
}

// Cara 2: MutexGuard — lock hidup selama guard dalam scope
func incrementAndRead() -> Int {
    var guard_ = counter.lock()   // lock acquired
    guard_.value += 1
    let result = guard_.value
    return result
    // guard_ keluar scope → lock released OTOMATIS
}

Use Case 3: Cryptographic Key Management

swift
import CryptoKit

// Private key yang harus di-zero saat tidak dibutuhkan
struct SecurePrivateKey: ~Copyable {
    // P256.Signing.PrivateKey sudah dikelola CryptoKit
    // Tapi kita bisa wrap untuk kontrol ownership yang lebih ketat
    private var key: P256.Signing.PrivateKey
    private let keyID: String
    
    init() {
        self.key = P256.Signing.PrivateKey()
        self.keyID = UUID().uuidString
        logKeyCreation(keyID)
    }
    
    // borrowing: sign tanpa memberikan akses ke raw key material
    borrowing func sign(_ data: Data) throws -> P256.Signing.ECDSASignature {
        return try key.signature(for: data)
    }
    
    // Public key aman untuk di-copy dan disebarkan
    borrowing func publicKey() -> P256.Signing.PublicKey {
        return key.publicKey
    }
    
    // Export — consuming karena setelah export key dianggap "habis"
    consuming func exportEncrypted(with wrappingKey: SymmetricKey) -> Data {
        // Encrypt dan return
        // Key material akan di-clear oleh deinit
        return Data()
    }
    
    deinit {
        // Key material di-clear dari memori
        // P256.PrivateKey sudah melakukan ini, tapi log kita tambahkan
        logKeyDestruction(keyID)
        // Dalam implementasi nyata: zero-out semua sensitive bytes
    }
    
    private func logKeyCreation(_ id: String) { print("Key created: \(id)") }
    private func logKeyDestruction(_ id: String) { print("Key destroyed: \(id)") }
}

// Penggunaan:
func signDocument(_ document: Data) async throws -> SignedDocument {
    // Key hanya ada dalam scope ini — tidak bisa bocor ke luar
    let key = SecurePrivateKey()
    
    let signature = try key.sign(document)
    let publicKey = key.publicKey()
    
    // let exportedKey = key  // ❌ ERROR: tidak bisa copy key
    
    return SignedDocument(
        data: document,
        signature: signature,
        signerPublicKey: publicKey
    )
    // key di-destroy di sini — zero-out dari memori
}

Use Case 4: Connection Pool dengan Guaranteed Return

swift
// Connection yang PASTI dikembalikan ke pool setelah dipakai

final class ConnectionPool {
    private var available: [Connection] = []
    private let mutex = NSLock()
    
    // Mengembalikan ticket — bukan connection langsung
    func acquire() -> PooledConnection? {
        mutex.lock()
        defer { mutex.unlock() }
        guard let connection = available.popLast() else { return nil }
        return PooledConnection(connection: connection, pool: self)
    }
    
    fileprivate func returnConnection(_ connection: Connection) {
        mutex.lock()
        defer { mutex.unlock() }
        available.append(connection)
    }
}

// Noncopyable: connection tidak bisa di-share atau di-copy
struct PooledConnection: ~Copyable {
    private let connection: Connection
    private weak var pool: ConnectionPool?
    
    fileprivate init(connection: Connection, pool: ConnectionPool) {
        self.connection = connection
        self.pool = pool
    }
    
    borrowing func query(_ sql: String) throws -> [Row] {
        try connection.execute(sql)
    }
    
    deinit {
        // PASTI dikembalikan ke pool — tidak ada connection leak
        pool?.returnConnection(connection)
    }
}

// Penggunaan:
func handleRequest(pool: ConnectionPool) async throws -> Response {
    guard let conn = pool.acquire() else {
        throw ServiceError.noConnectionAvailable
    }
    
    // conn tidak bisa di-copy, tidak bisa keluar scope tanpa dikembalikan ke pool
    let rows = try conn.query("SELECT * FROM users LIMIT 10")
    let response = Response(data: rows)
    
    return response
    // conn dikembalikan ke pool OTOMATIS di sini — tidak perlu manual return
}