 		}
 	}
 
+	/// Possible ways to complete a transaction
+	public enum TransactionCompletion {
+		/// The transaction should be committed
+		case commit
+		/// The transaction should be rolled back
+		case rollback
+	}
+
+	/// A series of database actions grouped into a transaction
+	///
+	/// - parameter database: A `Database` used for database access within the block
+	///
+	/// - returns: `.commit` if the transaction should be committed or `.rollback` if the transaction should be rolled back
+	public typealias TransactionBlock = (_ database: Database) throws -> TransactionCompletion
+
 	/// Submits an asynchronous read/write transaction to the database queue.
 	///
 	/// - parameter type: The type of transaction to perform
 	/// - parameter block: A closure performing the database operation
 	/// - parameter database: A `Database` used for database access within `block`
 	/// - parameter rollback: Whether to rollback the transaction after `block` completes
-	public func transaction(type: Database.TransactionType = .deferred, _ block: @escaping (_ database: Database, _ rollback: inout Bool) -> (Void)) {
+	public func transaction(type: Database.TransactionType = .deferred, _ block: @escaping TransactionBlock) {
 		queue.async(flags: .barrier) {
 			do {
 				try self.database.begin(type: type)
-				var rollback = false
-				block(self.database, &rollback)
-				try rollback ? self.database.rollback() : self.database.commit()
-			}
-			catch let error {
-				#if DEBUG
-					print(error)
-				#endif
+			}
+			catch let error {
+				os_log("Error beginning transaction: %{public}@", type: .error, error.localizedDescription);
+				return
+			}
+
+			do {
+				let action = try block(self.database)
+				switch action {
+				case .commit:
+					try self.database.commit()
+				case .rollback:
+					try self.database.rollback()
+				}
+			}
+			catch let error {
+				os_log("Error during transaction: %{public}@", type: .error, error.localizedDescription);
+				if !self.database.isInAutocommitMode {
+					try? self.database.rollback()
+				}
 			}
 		}
 	}

	/// Performs a synchronous read/write savepoint on the database.
	///
	/// - parameter block: A closure performing the database operation
	/// - parameter database: A `Database` used for database access within `block`
	/// - parameter rollback: Whether to rollback the savepoint after `block` completes
	///
	/// - throws: Any error thrown in `block` or an error if the savepoint could not be started, rolled back, or released
	///
	/// - returns: The value returned by `block`
	public func savepoint_sync<T>(block: (_ database: Database, _ rollback: inout Bool) throws -> (T)) rethrows -> T {
		return try queue.sync(flags: .barrier) {
			let savepointUUID = UUID().uuidString
			try database.begin(savepoint: savepointUUID)
			var rollback = false
			let result = try block(database, &rollback)
			try rollback ? database.rollback(to: savepointUUID) : database.release(savepoint: savepointUUID)
			return result
		}
	}
}

//

import Foundation
import os.log

/// A queue providing concurrent execution (multiple readers and one writer) of work items on a database.
///
