-
Notifications
You must be signed in to change notification settings - Fork 65
/
Copy pathClientSession.swift
157 lines (144 loc) · 6.99 KB
/
ClientSession.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import MongoSwift
/**
* A MongoDB client session.
* This class represents a logical session used for ordering sequential operations.
*
* To create a client session, use `startSession` or `withSession` on a `MongoClient`.
*
* If `causalConsistency` is not set to `false` when starting a session, read and write operations that use the session
* will be provided causal consistency guarantees depending on the read and write concerns used. Using "majority"
* read and write preferences will provide the full set of guarantees. See
* https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/#sessions for more details.
*
* e.g.
* ```
* let opts = MongoCollectionOptions(readConcern: .majority, writeConcern: .majority)
* let collection = database.collection("mycoll", options: opts)
* try client.withSession { session in
* try collection.insertOne(["x": 1], session: session)
* try collection.find(["x": 1], session: session)
* }
* ```
*
* To disable causal consistency, set `causalConsistency` to `false` in the `ClientSessionOptions` passed in to either
* `withSession` or `startSession`.
*
* - SeeAlso:
* - https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/#sessions
* - https://docs.mongodb.com/manual/core/causal-consistency-read-write-concerns/
*/
public final class ClientSession {
/// The client used to start this session.
public let client: MongoClient
internal let asyncSession: MongoSwift.ClientSession
/// The most recent cluster time seen by this session. This value will be nil if either of the following are true:
/// - No operations have been executed using this session and `advanceClusterTime` has not been called.
/// - This session has been ended.
public var clusterTime: BSONDocument? { self.asyncSession.clusterTime }
/// The operation time of the most recent operation performed using this session. This value will be nil if either
/// of the following are true:
/// - No operations have been performed using this session and `advanceOperationTime` has not been called.
/// - This session has been ended.
public var operationTime: BSONTimestamp? { self.asyncSession.operationTime }
/// The options used to start this session.
public var options: ClientSessionOptions? { self.asyncSession.options }
/// Initializes a new client session.
internal init(client: MongoClient, options: ClientSessionOptions?) {
self.client = client
self.asyncSession = client.asyncClient.startSession(options: options)
}
/// Ends the underlying async session.
internal func end() {
// we only call this method from places that we can't throw (deinit, defers) so we handle the error here
// instead. the async method will only fail if the async client, thread pool, or event loop group have been
// closed/ended. we manage the lifetimes of all of those ourselves, so if we hit the assertionFailure it's due
// to a bug in our own code.
do {
try self.asyncSession.end().wait()
} catch {
assertionFailure("Error ending async session: \(error)")
}
}
/// Cleans up internal state.
deinit {
// a repeated call to `end` is a no-op so it's ok to call this even if `end()` was already called explicitly.
self.end()
}
/**
* Advances the clusterTime for this session to the given time, if it is greater than the current clusterTime. If
* the session has been ended, or if the provided clusterTime is less than the current clusterTime, this method has
* no effect.
*
* - Parameters:
* - clusterTime: The session's new cluster time, as a `Document` like `["cluster time": Timestamp(...)]`
*/
public func advanceClusterTime(to clusterTime: BSONDocument) {
self.asyncSession.advanceClusterTime(to: clusterTime)
}
/**
* Advances the operationTime for this session to the given time if it is greater than the current operationTime.
* If the session has been ended, or if the provided operationTime is less than the current operationTime, this
* method has no effect.
*
* - Parameters:
* - operationTime: The session's new operationTime
*/
public func advanceOperationTime(to operationTime: BSONTimestamp) {
self.asyncSession.advanceOperationTime(to: operationTime)
}
/**
* Starts a multi-document transaction for all subsequent operations in this session.
*
* Any options provided in `options` will override the default transaction options for this session and any options
* inherited from `MongoClient`.
*
* Operations executed as part of the transaction will use the options specified on the transaction, and those
* options cannot be overridden at a per-operation level. Any options that overlap with the transaction options
* which can be specified at a per operation level (e.g. write concern) _will be ignored_ if specified. This
* includes options specified at the database or collection level on the object used to execute an operation.
*
* The transaction must be completed with `commitTransaction` or `abortTransaction`. An in-progress transaction is
* automatically aborted when `ClientSession.end()` is called.
*
* - Parameters:
* - options: The options to use when starting this transaction
*
* - Throws:
* - `MongoError.CommandError` if an error occurs that prevents the command from executing.
* - `MongoError.LogicError` if the session already has an in-progress transaction.
* - `MongoError.LogicError` if `startTransaction` is called on an ended session.
*
* - SeeAlso:
* - https://docs.mongodb.com/manual/core/transactions/
*/
public func startTransaction(options: TransactionOptions? = nil) throws {
try self.asyncSession.startTransaction(options: options).wait()
}
/**
* Commits a multi-document transaction for this session. Server and network errors are not ignored.
*
* - Throws:
* - `MongoError.CommandError` if an error occurs that prevents the command from executing.
* - `MongoError.LogicError` if the session has no in-progress transaction.
* - `MongoError.LogicError` if `commitTransaction` is called on an ended session.
*
* - SeeAlso:
* - https://docs.mongodb.com/manual/core/transactions/
*/
public func commitTransaction() throws {
try self.asyncSession.commitTransaction().wait()
}
/**
* Aborts a multi-document transaction for this session. Server and network errors are ignored.
*
* - Throws:
* - `MongoError.LogicError` if the session has no in-progress transaction.
* - `MongoError.LogicError` if `abortTransaction` is called on an ended session.
*
* - SeeAlso:
* - https://docs.mongodb.com/manual/core/transactions/
*/
public func abortTransaction() throws {
try self.asyncSession.abortTransaction().wait()
}
}