Skip to content

Commit 831935c

Browse files
committed
added more documentation
1 parent f402612 commit 831935c

File tree

5 files changed

+130
-23
lines changed

5 files changed

+130
-23
lines changed

README.md

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def application do
7575
end
7676

7777
defp deps do
78-
[{:mongodb_driver, "~> 0.5"}]
78+
[{:mongodb_driver, "~> 0.6"}]
7979
end
8080
```
8181

@@ -250,12 +250,51 @@ importing big volume of data.
250250
|> Stream.run()
251251
```
252252

253-
For more information see and check the test units for examples.
253+
For more information see:
254254
* [Mongo.UnorderedBulk](https://hexdocs.pm/mongodb_driver/Mongo.UnorderedBulk.html#content)
255255
* [Mongo.OrderedBulk](https://hexdocs.pm/mongodb_driver/Mongo.OrderedBulk.html#content)
256256
* [Mongo.BulkWrite](https://hexdocs.pm/mongodb_driver/Mongo.BulkWrite.html#content)
257257
* [Mongo.BulkOps](https://hexdocs.pm/mongodb_driver/Mongo.BulkOps.html#content)
258258

259+
and have a look at the test units as well.
260+
261+
### Transactions
262+
263+
Since MongoDB 4.x, transactions for multiple write operations are possible. The [Mongo.Session](https://hexdocs.pm/mongodb_driver/Mongo.Session.html#content) is responsible for the details and you can use a convenient api for transactions:
264+
265+
```elixir
266+
alias Mongo.Session
267+
268+
{:ok, ids} = Session.with_transaction(top, fn opts ->
269+
{:ok, %InsertOneResult{:inserted_id => id1}} = Mongo.insert_one(top, "dogs", %{name: "Greta"}, opts)
270+
{:ok, %InsertOneResult{:inserted_id => id2}} = Mongo.insert_one(top, "dogs", %{name: "Waldo"}, opts)
271+
{:ok, %InsertOneResult{:inserted_id => id3}} = Mongo.insert_one(top, "dogs", %{name: "Tom"}, opts)
272+
{:ok, [id1, id2, id3]}
273+
end, w: 1)
274+
```
275+
276+
It is also possible to get more control over the progress of the transaction:
277+
278+
```elixir
279+
alias Mongo.Session
280+
281+
{:ok, session} = Session.start_session(top, :write, [])
282+
:ok = Session.start_transaction(session)
283+
284+
Mongo.insert_one(top, "dogs", %{name: "Greta"}, session: session)
285+
Mongo.insert_one(top, "dogs", %{name: "Waldo"}, session: session)
286+
Mongo.insert_one(top, "dogs", %{name: "Tom"}, session: session)
287+
288+
:ok = Session.commit_transaction(session)
289+
:ok = Session.end_session(top, session)
290+
```
291+
292+
For more information see:
293+
294+
* [Mongo.Session](https://hexdocs.pm/mongodb_driver/Mongo.Session.html#content)
295+
296+
and have a look at the test units as well.
297+
259298
### Examples
260299

261300
Using `$and`

lib/mongo.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,9 @@ defmodule Mongo do
319319
:ok <- Session.end_implict_session(topology_pid, session) do
320320
result
321321
else
322-
{:new_connection, _server} -> issue_command(topology_pid, cmd, type, opts)
322+
{:new_connection, _server} ->
323+
:timer.sleep(1000)
324+
issue_command(topology_pid, cmd, type, opts)
323325
end
324326
end
325327

lib/mongo/bulk_write.ex

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,9 @@ defmodule Mongo.BulkWrite do
187187
:ok <- Session.end_implict_session(topology_pid, session) do
188188
result
189189
else
190-
{:new_connection, _server} -> write(topology_pid, bulk, opts)
190+
{:new_connection, _server} ->
191+
:timer.sleep(1000)
192+
write(topology_pid, bulk, opts)
191193
end
192194

193195
end
@@ -208,7 +210,9 @@ defmodule Mongo.BulkWrite do
208210

209211
result
210212
else
211-
{:new_connection, _server} -> write(topology_pid, bulk, opts)
213+
{:new_connection, _server} ->
214+
:timer.sleep(1000)
215+
write(topology_pid, bulk, opts)
212216
end
213217

214218
end

lib/mongo/session.ex

Lines changed: 79 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,48 @@
11
defmodule Mongo.Session do
22

33
@moduledoc """
4+
This module implements the details of the transactions api ([see specs](https://github.com/mongodb/specifications/blob/master/source/transactions/transactions.rst#committransaction)).
5+
It uses the `:gen_statem` behaviour ([A nice tutorial](https://andrealeopardi.com/posts/connection-managers-with-gen_statem/)) to manage the different states.
46
5-
For gen_statem look here
6-
* see https://github.com/mongodb/specifications/blob/master/source/transactions/transactions.rst#committransaction
7-
* see https://andrealeopardi.com/posts/connection-managers-with-gen_statem/
7+
In case of MongoDB 3.6 or greater the driver uses sessions for each operation. If no session is created the driver will create a so-called implict session. A session is a UUID-Number which
8+
is added to some operations. The sessions are used to manage the transaction state as well. In most situation you need not to create a session instance, so the interface of the driver is not changed.
9+
10+
In case of multiple insert statemantes you can use transaction (MongoDB 4.x) to be sure that all operations are grouped like a single operation. Prerequisites for transactions are:
11+
MongoDB 4.x must be used as replica set or cluster deployment. The collection used in the operations must already exist. Some operation are not allowed (For example: create index or call count).
12+
13+
## Example
14+
15+
alias Mongo.Session
16+
17+
{:ok, session} = Session.start_session(top, :write, [])
18+
:ok = Session.start_transaction(session)
19+
20+
Mongo.insert_one(top, "dogs", %{name: "Greta"}, session: session)
21+
Mongo.insert_one(top, "dogs", %{name: "Waldo"}, session: session)
22+
Mongo.insert_one(top, "dogs", %{name: "Tom"}, session: session)
23+
24+
:ok = Session.commit_transaction(session)
25+
:ok = Session.end_session(top, session)
26+
27+
First you start a explicit session and a transactions. Use need to use the session for each insert statement as an options with key `:session` otherwise the insert statement won't be
28+
executed in the transaction. After that you commit the transaction and end the session by calling `end_session`.
29+
30+
## Convenient API for Transactions
31+
32+
This method is responsible for starting a transaction, invoking a callback, and committing a transaction.
33+
The callback is expected to execute one or more operations with the transaction; however, that is not enforced.
34+
The callback is allowed to execute other operations not associated with the transaction.
35+
36+
## Example
37+
38+
{:ok, ids} = Session.with_transaction(top, fn opts ->
39+
{:ok, %InsertOneResult{:inserted_id => id1}} = Mongo.insert_one(top, "dogs", %{name: "Greta"}, opts)
40+
{:ok, %InsertOneResult{:inserted_id => id2}} = Mongo.insert_one(top, "dogs", %{name: "Waldo"}, opts)
41+
{:ok, %InsertOneResult{:inserted_id => id3}} = Mongo.insert_one(top, "dogs", %{name: "Tom"}, opts)
42+
{:ok, [id1, id2, id3]}
43+
end, w: 1)
44+
45+
If the callback is successfull then it returns a tupel with the keyword `:ok` and a used defined result like `{:ok, [id1, id2, id3]}`
846
"""
947

1048
@behaviour :gen_statem
@@ -27,6 +65,8 @@ defmodule Mongo.Session do
2765
# * `server_session` the server_session data
2866
# * `opts` options
2967
# * `implicit` true or false
68+
# * `causal_consistency` true orfalse
69+
# * `wire_version` current wire version to check if transactions are possible
3070
defstruct [conn: nil, server_session: nil, causal_consistency: false, operation_time: nil, implicit: false, wire_version: 0, opts: []]
3171

3272
@impl true
@@ -43,34 +83,45 @@ defmodule Mongo.Session do
4383
end
4484

4585
@doc """
46-
Start a new transation.
47-
"""
48-
@spec start_transaction(Session.t) :: :ok | {:error, term()}
49-
def start_transaction(pid) do
50-
:gen_statem.call(pid, {:start_transaction})
51-
end
86+
Start a new session for the `topology_pid`. You need to specify the `type`: `:read` for read and `:write` for write
87+
operations.
88+
89+
## Example
90+
{:ok, session} = Session.start_session(top, :write, [])
5291
53-
@doc """
54-
Start a new session
5592
"""
56-
def start_session(topology_pid, type, opts) do
93+
@spec start_session(GenServer.server, atom, keyword()) :: {:ok, Session.t} | {:error, term()}
94+
def start_session(topology_pid, type, opts \\ []) do
5795
with {:ok, session} <- Topology.checkout_session(topology_pid, type, :explicit, opts) do
5896
{:ok, session}
5997
else
60-
{:new_connection, _server} -> start_session(topology_pid, type, opts)
98+
{:new_connection, _server} ->
99+
:timer.sleep(1000)
100+
start_session(topology_pid, type, opts)
61101
end
62102
end
63103

64104
@doc """
65-
Start a new implicit session only if no explicit session exists.
105+
Start a new transation.
106+
"""
107+
@spec start_transaction(Session.t) :: :ok | {:error, term()}
108+
def start_transaction(pid) do
109+
:gen_statem.call(pid, {:start_transaction})
110+
end
111+
112+
@doc """
113+
Start a new implicit session only if no explicit session exists. It returns the session in the `opts` keyword list or
114+
creates a new one.
66115
"""
67116
def start_implicit_session(topology_pid, type, opts) do
68117
case Keyword.get(opts, :session, nil) do
69118
nil ->
70119
with {:ok, session} <- Topology.checkout_session(topology_pid, type, :implicit, opts) do
71120
{:ok, session}
72121
else
73-
{:new_connection, _server} -> start_implicit_session(topology_pid, type, opts)
122+
{:new_connection, _server} ->
123+
:timer.sleep(1000)
124+
start_implicit_session(topology_pid, type, opts)
74125
end
75126
session -> {:ok, session}
76127
end
@@ -166,9 +217,12 @@ defmodule Mongo.Session do
166217
end
167218

168219

220+
##
221+
# calling the function and wrapping it to catch exceptions
222+
#
169223
defp run_function(fun, opts) do
170224

171-
## warte max 120ms, ansonsten kill
225+
## todo wait max 120s
172226
try do
173227
fun.(opts)
174228
rescue
@@ -177,14 +231,23 @@ defmodule Mongo.Session do
177231

178232
end
179233

234+
@doc """
235+
Return the connection used in the session
236+
"""
180237
def connection(pid) do
181238
:gen_statem.call(pid, {:connection})
182239
end
183240

241+
@doc """
242+
Return the server session used in the session
243+
"""
184244
def server_session(pid) do
185245
:gen_statem.call(pid, {:server_session})
186246
end
187247

248+
@doc"""
249+
Check if the session is alive
250+
"""
188251
def alive?(nil), do: false
189252
def alive?(pid), do: Process.alive?(pid)
190253

@@ -353,7 +416,6 @@ defmodule Mongo.Session do
353416
:ok
354417
end
355418

356-
357419
##
358420
# create the readConcern options
359421
#

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule Mongodb.Mixfile do
22
use Mix.Project
33

4-
@version "0.5.7"
4+
@version "0.6.0"
55

66
def project() do
77
[app: :mongodb_driver,

0 commit comments

Comments
 (0)