Skip to content

Commit 95a1020

Browse files
committed
Updates to support MongoDB 5.1+
In MongoDB 3.6, the `OP_MSG` and `OP_COMPRESSED` opcodes were added to the wired message protocol. `OP_MSG` was added to standardize the MongoDB message format, and `OP_COMPRESSED` takes things a step further by compressing the message to increase network efficiency. As of MongoDB 5.1, the old opcodes were removed, and all messages are sent with either `OP_MSG` or `OP_COMPRESSED`. Because this driver did not have the ability to send messages with the `OP_MSG` opcode, it couldn't be used with any versions of MongoDB greater than 5.1. To bring this driver into the modern era, this PR ports over the changes made in the [emqx fork](https://github.com/emqx/mongodb-erlang/) of this driver to support the `OP_MSG` opcode. Additionally, there are some slightly unrelated, but relevant changes in this PR as well such as fixing some dialyzer errors and updating the GitHub actions workflow definitions to get the tests up and running again.
1 parent f4d4533 commit 95a1020

26 files changed

Lines changed: 792 additions & 107 deletions

.github/workflows/dialyzer.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ on:
77

88
jobs:
99
dialyzer:
10-
runs-on: ubuntu-20.04
10+
runs-on: ubuntu-24.04
1111
container:
1212
image: erlang:24-slim
1313
steps:
14-
- uses: actions/checkout@v2.0.0
14+
- uses: actions/checkout@v4
1515
- name: Cache PLTs
1616
id: cache-plts
17-
uses: actions/cache@v2
17+
uses: actions/cache@v4
1818
with:
1919
path: ~/.cache/rebar3/
2020
key: ${{ runner.os }}-erlang-${{ hashFiles(format('{0}{1}', github.workspace, '/rebar.lock')) }}

.github/workflows/test.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,37 @@ on:
77

88
jobs:
99
test:
10-
runs-on: ubuntu-20.04
10+
runs-on: ubuntu-24.04
1111
strategy:
1212
matrix:
13-
erlang: [22, 23]
13+
erlang: [24, 25]
1414
mongodb: ["4.4.8", "5.0.2"]
1515
container:
1616
image: erlang:${{ matrix.erlang }}
1717
steps:
18-
- uses: actions/checkout@v2.0.0
18+
- uses: actions/checkout@v4
1919
- run: ./scripts/install_mongo_debian.sh ${{ matrix.mongodb }}
2020
- run: ./scripts/start_mongo_single_node.sh
2121
- run: ./scripts/start_mongo_cluster.sh
2222
- run: ./rebar3 eunit
2323
- run: ./rebar3 ct
2424
- name: Archive Replica Set Logs
25-
uses: actions/upload-artifact@v2
25+
uses: actions/upload-artifact@v4
2626
if: failure()
2727
with:
28-
name: mongodb_replica_set_logs
28+
name: erlang-${{ matrix.erlang }}-mongodb-${{ matrix.mongodb }}-mongodb_replica_set_logs
2929
path: rs0-logs
3030
retention-days: 1
3131
- name: Archive Single Node Log
32-
uses: actions/upload-artifact@v2
32+
uses: actions/upload-artifact@v4
3333
if: failure()
3434
with:
35-
name: single_node.log
35+
name: erlang-${{ matrix.erlang }}-mongodb-${{ matrix.mongodb }}-single_node.log
3636
path: single_node.log
3737
retention-days: 1
3838
- name: CT Logs
39-
uses: actions/upload-artifact@v2
39+
uses: actions/upload-artifact@v4
4040
with:
41-
name: ct_logs
41+
name: erlang-${{ matrix.erlang }}-mongodb-${{ matrix.mongodb }}-ct_logs
4242
path: _build/test/logs/
4343
retention-days: 5

.github/workflows/test_coverage.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,39 @@ on:
77

88
jobs:
99
test_coverage:
10-
runs-on: ubuntu-20.04
10+
runs-on: ubuntu-24.04
1111
container:
12-
image: erlang:23
12+
image: erlang:24
1313
steps:
14-
- uses: actions/checkout@v2.0.0
14+
- uses: actions/checkout@v4
1515
- run: ./scripts/install_mongo_debian.sh 5.0.2
1616
- run: ./scripts/start_mongo_single_node.sh
1717
- run: ./scripts/start_mongo_cluster.sh
1818
- run: ./rebar3 eunit --cover --cover_export_name eunit.coverdata
1919
- run: ./rebar3 ct --cover --cover_export_name ct.coverdata
2020
- run: rebar3 cover --verbose
2121
- name: Archive Replica Set Logs
22-
uses: actions/upload-artifact@v2
22+
uses: actions/upload-artifact@v4
2323
if: failure()
2424
with:
2525
name: mongodb_replica_set_logs
2626
path: rs0-logs
2727
retention-days: 1
2828
- name: Archive Single Node Log
29-
uses: actions/upload-artifact@v2
29+
uses: actions/upload-artifact@v4
3030
if: failure()
3131
with:
3232
name: single_node.log
3333
path: single_node.log
3434
retention-days: 1
3535
- name: Coverage Report
36-
uses: actions/upload-artifact@v2
36+
uses: actions/upload-artifact@v4
3737
with:
3838
name: Coverage Report
3939
path: _build/test/cover/
4040
retention-days: 5
4141
- name: CT Logs
42-
uses: actions/upload-artifact@v2
42+
uses: actions/upload-artifact@v4
4343
with:
4444
name: ct_logs
4545
path: _build/test/logs/

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ variables-ct*
66
*.iml
77
data
88
_build
9-
.idea
9+
.idea
10+
*.log
11+
rebar.lock
12+
*.crashdump

README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,30 @@ If you are choosing between using
4242
[mongos](https://docs.mongodb.com/manual/reference/program/mongos/) and
4343
using mongo shard with `mongo_api` - prefer mongos and use `mc_worker_api`.
4444

45+
In MongoDB 3.6, the `OP_MSG` and `OP_COMPRESSED` opcodes were added to the wired
46+
message protocol. `OP_MSG` was added to standardize the MongoDB message format,
47+
and `OP_COMPRESSED` takes things a step further by compressing the message to increase
48+
network efficiency. As of MongoDB 5.1, the old opcodes were deprecated, and all
49+
messages are sent with either `OP_MSG` or `OP_COMPRESSED`.
50+
51+
By default, this driver tries to automatically detect which MongoDB messaging protocol
52+
to use - the legacy one that existed before the `OP_MSG` opcode was introduced, or the
53+
modern messaging protocol based on `OP_MSG`. This is accomplished by setting the default
54+
value for the `use_legacy_protocol` env variable to `auto`. One can force the driver to
55+
use the legacy opcodes or the `OP_MSG`opcode by setting the application env
56+
`use_legacy_protocol` to `true` or `false` (for example by calling
57+
`application:set_env(mongodb, use_legacy_protocol, false)`).
58+
59+
It's also possible to define the usage of legacy protocol on a per-connection basis.
60+
Simple pass `{use_legacy_protocol, false | true}` to mc_worker start options.
61+
62+
As the `OP_MSG` opcode has existed in MongoDB since 3.6, and the driver defaults to auto-
63+
detecting which set of opcodes to use, ALL of the messages will be sent using the `OP_MSG`
64+
opcode if you are using a version for MongoDB greater 3.6 unless you explicitly set the
65+
`use_legacy_protocol` variable to `true`.
66+
4567
mc_worker_api -- direct connection client
4668
---------------------------------
47-
4869
### Connecting
4970
To connect to a database `test` on mongodb server listening on
5071
`localhost:27017` (or any address & port of your choosing)

include/mongo_protocol.hrl

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
-type colldb() :: collection() | {database(), collection()}.
99
-type collection() :: binary() | atom(). % without db prefix
1010
-type database() :: binary() | atom().
11+
-type command() :: insert | update | delete.
1112

1213

1314
%% write
@@ -45,6 +46,24 @@
4546
projector = #{} :: mc_worker_api:projector()
4647
}).
4748

49+
-record(op_msg_write_op, {
50+
command :: command(),
51+
collection :: colldb(),
52+
database :: undefined | mc_worker_api:database(),
53+
extra_fields = [] :: bson:document() | nonempty_list({binary(),any()}),
54+
documents_name = <<"documents">> :: bson:utf8(),
55+
documents = [] :: any()
56+
}).
57+
58+
-record(op_msg_response, {
59+
response_doc :: map()
60+
}).
61+
62+
-record(op_msg_command, {
63+
database :: undefined | mc_worker_api:database(),
64+
command_doc :: bson:document() | nonempty_list({binary(),any()})
65+
}).
66+
4867
-record(getmore, {
4968
collection :: colldb(),
5069
batchsize = 0 :: mc_worker_api:batchsize(),
@@ -73,9 +92,9 @@
7392
-record(reply, {
7493
cursornotfound :: boolean(),
7594
queryerror :: boolean(),
76-
awaitcapable = false :: boolean(),
95+
awaitcapable = false :: boolean() | undefined,
7796
cursorid :: mc_worker_api:cursorid(),
78-
startingfrom = 0 :: integer(),
97+
startingfrom = 0 :: integer() | undefined,
7998
documents :: [map()]
8099
}).
81100
-endif.

include/mongo_types.hrl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,24 @@
3131
| {ssl, boolean()}
3232
| {ssl_opts, proplists:proplist()}
3333
| {register, atom() | fun()}.
34+
-type socket() :: gen_tcp:socket() | ssl:sslsocket().
3435
-type write_mode() :: unsafe | safe | {safe, bson:document()}.
3536
-type read_mode() :: master | slave_ok.
3637
-type service() :: {Host :: inet:hostname() | inet:ip_address(), Post :: 0..65535}.
3738
-type options() :: [option()].
3839
-type option() :: {timeout, timeout()} | {ssl, boolean()} | ssl | {database, database()} | {read_mode, read_mode()} | {write_mode, write_mode()}.
3940
-type cursor() :: pid().
40-
-type query() :: #query{}.
41-
-endif.
41+
-type query() :: #'query'{}.
42+
-type op_msg_command() :: #op_msg_command{}.
43+
-type op_msg_write_op() :: #op_msg_write_op{}.
44+
-type op_msg_response() :: #op_msg_response{}.
45+
-type request() :: query()
46+
| op_msg_command()
47+
| op_msg_write_op()
48+
| #killcursor{}
49+
| #insert{}
50+
| #update{}
51+
| #delete{}
52+
| #getmore{}
53+
| #ensure_index{}.
54+
-endif.

include/mongoc.hrl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@
7676
| {password, binary()}
7777
| {w_mode, mc_worker_api:write_mode()}.
7878
-type readprefs() :: [readpref()].
79-
-type readpref() :: #{rp_mode => readmode()}
79+
-type readpref() :: #{rp_mode => readmode()} | #{mode => binary()} | #{mode => binary(), tags => tuple()}
8080
|{rp_tags, [tuple()]}.
8181
-type reason() :: atom().
8282

83-
-endif.
83+
-endif.

0 commit comments

Comments
 (0)