Skip to content

Commit 85b2e11

Browse files
committed
Relocate DatabaseSnapshot documentation
1 parent c5a0598 commit 85b2e11

File tree

6 files changed

+110
-100
lines changed

6 files changed

+110
-100
lines changed

GRDB/Core/DatabasePool.swift

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,37 @@ import UIKit
66

77
/// A database connection that allows concurrent accesses to an SQLite database.
88
///
9-
/// Unless ``Configuration/readonly``, a database pool opens an SQLite database
9+
/// ## Overview
10+
///
11+
/// Unless ``Configuration/readonly``, a `DatabasePool` opens an SQLite database
1012
/// in the [WAL mode](https://sqlite.org/wal.html).
1113
///
1214
/// It creates one writer SQLite connection, and a pool of up to
1315
/// ``Configuration/maximumReaderCount`` read-only SQLite connections. All
1416
/// write accesses are executed in a serial **writer dispatch queue**. All
1517
/// read accesses are executed in **reader dispatch queues** (one per read-only
16-
/// SQLite connection).
18+
/// SQLite connection). SQLite connections are closed when the `DatabasePool`
19+
/// is deallocated.
1720
///
1821
/// See <doc:Concurrency> for more information about concurrent
1922
/// database accesses.
2023
///
21-
/// A database pool inherits most of its database access methods from the
24+
/// ## Usage
25+
///
26+
/// ```swift
27+
/// let dbPool = try DatabasePool(path: "/path/to/database.sqlite")
28+
///
29+
/// let playerCount = try dbPool.read { db in
30+
/// try Player.fetchCount(db)
31+
/// }
32+
///
33+
/// let newPlayerCount = try dbPool.write { db -> Int in
34+
/// try Player(name: "Arthur").insert(db)
35+
/// return try Player.fetchCount(db)
36+
/// }
37+
/// ```
38+
///
39+
/// `DatabasePool` inherits most of its database access methods from the
2240
/// ``DatabaseReader`` and ``DatabaseWriter`` protocols. It defines a few
2341
/// specific database access methods as well.
2442
///
@@ -817,37 +835,15 @@ extension DatabasePool {
817835
/// The returned snapshot sees an unchanging database content, as it existed
818836
/// at the moment it was created.
819837
///
820-
/// When you want to control the latest committed changes seen by a
821-
/// snapshot, create it from the pool's writer dispatch queue. Compare:
838+
/// It is a programmer error to create a snapshot from the writer dispatch
839+
/// queue when a transaction is opened:
822840
///
823841
/// ```swift
824-
/// let snapshot1 = try dbPool.writeWithoutTransaction { db -> DatabaseSnapshot in
842+
/// try dbPool.write { db in
825843
/// try Player.deleteAll()
826-
/// return try dbPool.makeSnapshot()
827-
/// }
828-
/// // <- Other threads may modify the database here
829-
/// let snapshot2 = try dbPool.makeSnapshot()
830-
///
831-
/// try snapshot1.read { db in
832-
/// // Guaranteed to be zero
833-
/// try Player.fetchCount(db)
834-
/// }
835-
///
836-
/// try snapshot2.read { db in
837-
/// // Could be anything
838-
/// try Player.fetchCount(db)
839-
/// }
840-
/// ```
841-
///
842-
/// It is forbidden to create a snapshot from the writer dispatch queue when
843-
/// a transaction is opened, because it is likely a programmer error:
844844
///
845-
/// ```swift
846-
/// try dbPool.writeInTransaction { db in
847-
/// try Player.deleteAll()
848845
/// // fatal error: makeSnapshot() must not be called from inside a transaction
849846
/// let snapshot = try dbPool.makeSnapshot()
850-
/// return .commit
851847
/// }
852848
/// ```
853849
///
@@ -868,11 +864,6 @@ extension DatabasePool {
868864
/// let snapshot = try dbPool.makeSnapshot()
869865
/// }
870866
/// ```
871-
///
872-
/// You can create as many snapshots as you need, regardless of the maximum
873-
/// number of reader connections in the pool.
874-
///
875-
/// Related SQLite documentation: <https://sqlite.org/isolation.html>
876867
public func makeSnapshot() throws -> DatabaseSnapshot {
877868
// Sanity check
878869
if writer.onValidQueue {

GRDB/Core/DatabaseQueue.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,28 @@ import UIKit
66

77
/// A database connection that serializes accesses to an SQLite database.
88
///
9-
/// A database queue creates one single SQLite connection. All database accesses
10-
/// are executed in a serial **writer dispatch queue**.
9+
/// ## Overview
1110
///
12-
/// A database queue inherits most of its database access methods from the
11+
/// A `DatabaseQueue` creates one single SQLite connection. All database
12+
/// accesses are executed in a serial **writer dispatch queue**. The SQLite
13+
/// connection is closed when the `DatabaseQueue` is deallocated.
14+
///
15+
/// ```swift
16+
/// let dbQueue = try DatabaseQueue(path: "/path/to/database.sqlite")
17+
///
18+
/// let playerCount = try dbQueue.read { db in
19+
/// try Player.fetchCount(db)
20+
/// }
21+
///
22+
/// let newPlayerCount = try dbQueue.write { db -> Int in
23+
/// try Player(name: "Arthur").insert(db)
24+
/// return try Player.fetchCount(db)
25+
/// }
26+
/// ```
27+
///
28+
/// ## Usage
29+
///
30+
/// `DatabaseQueue` inherits most of its database access methods from the
1331
/// ``DatabaseReader`` and ``DatabaseWriter`` protocols. It defines a few
1432
/// specific database access methods as well.
1533
///

GRDB/Core/DatabaseReader.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ import Dispatch
2121
///
2222
/// ### Reading from the Database
2323
///
24-
/// - ``asyncRead(_:)``
2524
/// - ``read(_:)-3806d``
2625
/// - ``read(_:)-4w6gy``
2726
/// - ``readPublisher(receiveOn:value:)``
27+
/// - ``asyncRead(_:)``
2828
///
2929
/// ### Unsafe Methods
3030
///
31-
/// - ``asyncUnsafeRead(_:)``
3231
/// - ``unsafeRead(_:)-5i7tf``
3332
/// - ``unsafeRead(_:)-11mk0``
3433
/// - ``unsafeReentrantRead(_:)``
34+
/// - ``asyncUnsafeRead(_:)``
3535
///
3636
/// ### Other Database Operations
3737
///

GRDB/Core/DatabaseSnapshot.swift

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,64 @@ import Dispatch
33
/// A database connection that sees an unchanging database content, as it
44
/// existed at the moment it was created.
55
///
6+
/// ## Overview
7+
///
8+
/// A `DatabaseSnapshot` creates one single SQLite connection. All database
9+
/// accesses are executed in a serial **reader dispatch queue**. The SQLite
10+
/// connection is closed when the `DatabaseSnapshot` is deallocated.
11+
///
12+
/// `DatabaseSnapshot` never sees any database modification during all its
13+
/// lifetime, and yet it doesn't prevent database updates. This "magic" is made
14+
/// possible by SQLite's WAL mode. See
15+
/// [Isolation In SQLite](https://sqlite.org/isolation.html) for
16+
/// more information.
17+
///
18+
/// ## Usage
19+
///
620
/// You create instances of `DatabaseSnapshot` from a ``DatabasePool``, with
7-
/// ``DatabasePool/makeSnapshot()``.
21+
/// ``DatabasePool/makeSnapshot()``. The number of snapshots is unlimited,
22+
/// regardless of the ``Configuration/maximumReaderCount`` configuration:
23+
///
24+
/// ```swift
25+
/// let dbPool = try DatabasePool(path: "/path/to/database.sqlite")
26+
/// let snapshot = try dbPool.makeSnapshot()
27+
/// let playerCount = try snapshot.read { db in
28+
/// try Player.fetchCount(db)
29+
/// }
30+
/// ```
31+
///
32+
/// When you want to control the database state seen by a snapshot,
33+
/// create the snapshot from within a write access, outside of any transaction.
34+
///
35+
/// For example, compare the two snapshots below. The first one is guaranteed to
36+
/// see an empty table of players, because is is created after all players have
37+
/// been deleted, and from the serialized writer dispatch queue which prevents
38+
/// any concurrent write. The second is created without this concurrency
39+
/// protection, which means that some other threads may already have created
40+
/// some players:
41+
///
42+
/// ```swift
43+
/// let snapshot1 = try dbPool.writeWithoutTransaction { db -> DatabaseSnapshot in
44+
/// try db.inTransaction {
45+
/// try Player.deleteAll()
46+
/// return .commit
47+
/// }
48+
///
49+
/// return dbPool.makeSnapshot()
50+
/// }
51+
///
52+
/// // <- Other threads may have created some players here
53+
/// let snapshot2 = try dbPool.makeSnapshot()
854
///
9-
/// A database snapshot creates one single SQLite connection. All database
10-
/// accesses are executed in a serial dispatch queue.
55+
/// // Guaranteed to be zero
56+
/// let count1 = try snapshot1.read(Player.fetchCount)
1157
///
12-
/// A database snapshot inherits all of its database access methods from the
13-
/// ``DatabaseReader`` protocol.
58+
/// // Could be anything
59+
/// let count2 = try snapshot2.read(Player.fetchCount)
60+
/// ```
1461
///
15-
/// Related SQLite documentation: <https://sqlite.org/isolation.html>
62+
/// `DatabaseSnapshot` inherits its database access methods from the
63+
/// ``DatabaseReader`` protocols.
1664
public final class DatabaseSnapshot {
1765
private let serializedDatabase: SerializedDatabase
1866

GRDB/Core/DatabaseWriter.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,26 @@ import Dispatch
2121
///
2222
/// ### Writing into the Database
2323
///
24-
/// - ``asyncWrite(_:completion:)``
25-
/// - ``asyncWriteWithoutTransaction(_:)``
2624
/// - ``write(_:)-76inz``
2725
/// - ``write(_:)-88g7e``
2826
/// - ``writePublisher(receiveOn:updates:)``
2927
/// - ``writePublisher(receiveOn:updates:thenRead:)``
3028
/// - ``writeWithoutTransaction(_:)-4qh1w``
3129
/// - ``writeWithoutTransaction(_:)-tckw``
32-
///
33-
/// ### Reading from the Database
34-
///
35-
/// - ``concurrentRead(_:)``
36-
/// - ``spawnConcurrentRead(_:)``
37-
/// - ``DatabaseFuture``
30+
/// - ``asyncWrite(_:completion:)``
31+
/// - ``asyncWriteWithoutTransaction(_:)``
3832
///
3933
/// ### Exclusive Access to the Database
4034
///
41-
/// - ``asyncBarrierWriteWithoutTransaction(_:)``
4235
/// - ``barrierWriteWithoutTransaction(_:)-280j1``
4336
/// - ``barrierWriteWithoutTransaction(_:)-7u4xw``
37+
/// - ``asyncBarrierWriteWithoutTransaction(_:)``
38+
///
39+
/// ### Reading from the Latest Committed Database State
40+
///
41+
/// - ``concurrentRead(_:)``
42+
/// - ``spawnConcurrentRead(_:)``
43+
/// - ``DatabaseFuture``
4444
///
4545
/// ### Observing Database Transactions
4646
///

GRDB/Documentation.docc/Concurrency.md

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -334,53 +334,6 @@ In the illustration below, the striped band shows the delay needed for the readi
334334

335335
Types that conform to ``TransactionObserver`` can also use those methods in their ``TransactionObserver/databaseDidCommit(_:)`` method, in order to process database changes without blocking other threads that want to write into the database.
336336

337-
## Database Snapshots
338-
339-
**``DatabasePool`` can take snapshots.** A database snapshot sees an unchanging database content, as it existed at the moment it was created.
340-
341-
"Unchanging" means that a snapshot never sees any database modifications during all its lifetime. And yet it doesn't prevent database updates. This "magic" is made possible by SQLite's WAL mode (see [Isolation In SQLite](https://sqlite.org/isolation.html)).
342-
343-
```swift
344-
let snapshot = try dbPool.makeSnapshot()
345-
```
346-
347-
You can create as many snapshots as you need, regardless of the maximum number of readers defined by the ``Configuration/maximumReaderCount`` configuration. A snapshot connection is closed when the snapshot is deallocated.
348-
349-
**A snapshot can be used from any thread.** It has the same database access methods as database queues and pools, defined by the ``DatabaseReader`` protocol:
350-
351-
```swift
352-
let playerCount = try snapshot.read { db in
353-
try Player.fetchCount(db)
354-
}
355-
```
356-
357-
When you want to control the latest committed changes seen by a snapshot, create the snapshot from within a write, outside of any transaction:
358-
359-
```swift
360-
let snapshot1 = try dbPool.writeWithoutTransaction { db -> DatabaseSnapshot in
361-
try db.inTransaction {
362-
// delete all players
363-
try Player.deleteAll()
364-
return .commit
365-
}
366-
367-
// <- not in a transaction here
368-
return dbPool.makeSnapshot()
369-
}
370-
// <- Other threads may modify the database here
371-
let snapshot2 = try dbPool.makeSnapshot()
372-
373-
try snapshot1.read { db in
374-
// Guaranteed to be zero
375-
try Player.fetchCount(db)
376-
}
377-
378-
try snapshot2.read { db in
379-
// Could be anything
380-
try Player.fetchCount(db)
381-
}
382-
```
383-
384337
## Topics
385338

386339
### Database Connections with Concurrency Guarantees

0 commit comments

Comments
 (0)