Skip to content

Commit d5f73e1

Browse files
allow snapshot reads through sessions and add prose test (#807)
* CDRIVER-4034 Snapshot reads * CDRIVER-4037 Prose test Co-authored-by: Kevin Albertson <[email protected]>
1 parent c15c346 commit d5f73e1

17 files changed

+1505
-58
lines changed

src/libmongoc/doc/mongoc_session_opt_t.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ See the example code for :symbol:`mongoc_session_opts_set_causal_consistency`.
3030
mongoc_session_opts_set_causal_consistency
3131
mongoc_session_opts_get_default_transaction_opts
3232
mongoc_session_opts_set_default_transaction_opts
33+
mongoc_session_opts_get_snapshot
34+
mongoc_session_opts_set_snapshot
3335
mongoc_session_opts_get_transaction_opts
3436
mongoc_session_opts_clone
3537
mongoc_session_opts_destroy
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
:man_page: mongoc_session_opts_get_snapshot
2+
3+
mongoc_session_opts_get_snapshot()
4+
==================================
5+
6+
TODO CDRIVER-4036: Document this function and give example code.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
:man_page: mongoc_session_opts_set_snapshot
2+
3+
mongoc_session_opts_set_snapshot()
4+
==================================
5+
6+
TODO CDRIVER-4036: Document this function and give example code.

src/libmongoc/src/mongoc/mongoc-client-session-private.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ struct _mongoc_transaction_opt_t {
4141
typedef enum {
4242
MONGOC_SESSION_NO_OPTS = 0,
4343
MONGOC_SESSION_CAUSAL_CONSISTENCY = (1 << 0),
44+
MONGOC_SESSION_SNAPSHOT = (2 << 0),
4445
} mongoc_session_flag_t;
4546

4647
struct _mongoc_session_opt_t {
@@ -83,6 +84,9 @@ struct _mongoc_client_session_t {
8384
uint32_t client_generation;
8485
uint32_t server_id;
8586
bson_t *recovery_token;
87+
uint32_t snapshot_time_timestamp;
88+
uint32_t snapshot_time_increment;
89+
bool snapshot_time_set;
8690

8791
/* For testing only */
8892
int64_t with_txn_timeout_ms;
@@ -100,6 +104,7 @@ _mongoc_cluster_time_greater (const bson_t *new, const bson_t *old);
100104
void
101105
_mongoc_client_session_handle_reply (mongoc_client_session_t *session,
102106
bool is_acknowledged,
107+
const char *cmd_name,
103108
const bson_t *reply);
104109

105110
mongoc_server_session_t *
@@ -143,6 +148,7 @@ void
143148
_mongoc_client_session_append_read_concern (const mongoc_client_session_t *cs,
144149
const bson_t *user_read_concern,
145150
bool is_read_command,
151+
const char *cmd_name,
146152
bson_t *cmd);
147153

148154
void
@@ -152,5 +158,12 @@ void
152158
_mongoc_client_session_pin (mongoc_client_session_t *session,
153159
uint32_t server_id);
154160

161+
void
162+
_mongoc_client_session_set_snapshot_time (mongoc_client_session_t *session,
163+
uint32_t t,
164+
uint32_t i);
165+
166+
void
167+
_mongoc_client_session_clear_snapshot_time (mongoc_client_session_t *session);
155168

156169
#endif /* MONGOC_CLIENT_SESSION_PRIVATE_H */

src/libmongoc/src/mongoc/mongoc-client-session.c

Lines changed: 141 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -395,18 +395,25 @@ mongoc_transaction_opts_get_read_prefs (const mongoc_transaction_opt_t *opts)
395395
return opts->read_prefs;
396396
}
397397

398-
399-
mongoc_session_opt_t *
400-
mongoc_session_opts_new (void)
398+
bool
399+
mongoc_session_opts_get_causal_consistency (const mongoc_session_opt_t *opts)
401400
{
402-
mongoc_session_opt_t *opts = bson_malloc0 (sizeof (mongoc_session_opt_t));
401+
ENTRY;
403402

404-
/* Driver Sessions Spec: causal consistency is true by default */
405-
mongoc_session_opts_set_causal_consistency (opts, true);
403+
BSON_ASSERT (opts);
406404

407-
return opts;
405+
RETURN (!!(opts->flags & MONGOC_SESSION_CAUSAL_CONSISTENCY));
408406
}
409407

408+
bool
409+
mongoc_session_opts_get_snapshot (const mongoc_session_opt_t *opts)
410+
{
411+
ENTRY;
412+
413+
BSON_ASSERT (opts);
414+
415+
RETURN (!!(opts->flags & MONGOC_SESSION_SNAPSHOT));
416+
}
410417

411418
void
412419
mongoc_session_opts_set_causal_consistency (mongoc_session_opt_t *opts,
@@ -425,16 +432,35 @@ mongoc_session_opts_set_causal_consistency (mongoc_session_opt_t *opts,
425432
EXIT;
426433
}
427434

428-
bool
429-
mongoc_session_opts_get_causal_consistency (const mongoc_session_opt_t *opts)
435+
void
436+
mongoc_session_opts_set_snapshot (mongoc_session_opt_t *opts, bool snapshot)
430437
{
431438
ENTRY;
432439

433440
BSON_ASSERT (opts);
434441

435-
RETURN (!!(opts->flags & MONGOC_SESSION_CAUSAL_CONSISTENCY));
442+
if (snapshot) {
443+
opts->flags |= MONGOC_SESSION_SNAPSHOT;
444+
} else {
445+
opts->flags &= ~MONGOC_SESSION_SNAPSHOT;
446+
}
447+
448+
EXIT;
436449
}
437450

451+
mongoc_session_opt_t *
452+
mongoc_session_opts_new (void)
453+
{
454+
mongoc_session_opt_t *opts = bson_malloc0 (sizeof (mongoc_session_opt_t));
455+
456+
/* Driver Sessions Spec: causal consistency is true by default */
457+
mongoc_session_opts_set_causal_consistency (opts, true);
458+
459+
/* Snapshot Reads Spec: snapshot is false by default */
460+
mongoc_session_opts_set_snapshot (opts, false);
461+
462+
return opts;
463+
}
438464

439465
void
440466
mongoc_session_opts_set_default_transaction_opts (
@@ -606,21 +632,30 @@ _mongoc_cluster_time_greater (const bson_t *new, const bson_t *old)
606632
void
607633
_mongoc_client_session_handle_reply (mongoc_client_session_t *session,
608634
bool is_acknowledged,
635+
const char *cmd_name,
609636
const bson_t *reply)
610637
{
611638
bson_iter_t iter;
639+
bson_iter_t cursor_iter;
612640
uint32_t len;
613641
const uint8_t *data;
614642
bson_t cluster_time;
615-
uint32_t t;
616-
uint32_t i;
643+
uint32_t operation_t;
644+
uint32_t operation_i;
645+
uint32_t snapshot_t;
646+
uint32_t snapshot_i;
647+
bool is_find_aggregate_distinct;
617648

618649
BSON_ASSERT (session);
619650

620651
if (!reply || !bson_iter_init (&iter, reply)) {
621652
return;
622653
}
623654

655+
is_find_aggregate_distinct =
656+
(!strcmp (cmd_name, "find") || !strcmp (cmd_name, "aggregate") ||
657+
!strcmp (cmd_name, "distinct"));
658+
624659
if (mongoc_error_has_label (reply, "TransientTransactionError")) {
625660
/* Transaction Spec: "Drivers MUST unpin a ClientSession when a command
626661
* within a transaction, including commitTransaction and abortTransaction,
@@ -639,8 +674,39 @@ _mongoc_client_session_handle_reply (mongoc_client_session_t *session,
639674
mongoc_client_session_advance_cluster_time (session, &cluster_time);
640675
} else if (!strcmp (bson_iter_key (&iter), "operationTime") &&
641676
BSON_ITER_HOLDS_TIMESTAMP (&iter) && is_acknowledged) {
642-
bson_iter_timestamp (&iter, &t, &i);
643-
mongoc_client_session_advance_operation_time (session, t, i);
677+
bson_iter_timestamp (&iter, &operation_t, &operation_i);
678+
mongoc_client_session_advance_operation_time (
679+
session, operation_t, operation_i);
680+
} else if (is_find_aggregate_distinct &&
681+
!strcmp (bson_iter_key (&iter), "atClusterTime") &&
682+
mongoc_session_opts_get_snapshot (&session->opts) &&
683+
!session->snapshot_time_set) {
684+
/* If command is "find", "aggregate" or "distinct", atClusterTime is on
685+
* top level of reply, snapshot is enabled for the session, and
686+
* snapshot_time has not already been set, set it. */
687+
bson_iter_timestamp (&iter, &snapshot_t, &snapshot_i);
688+
_mongoc_client_session_set_snapshot_time (
689+
session, snapshot_t, snapshot_i);
690+
} else if (is_find_aggregate_distinct &&
691+
!strcmp (bson_iter_key (&iter), "cursor") &&
692+
mongoc_session_opts_get_snapshot (&session->opts) &&
693+
!session->snapshot_time_set) {
694+
/* If command is "find", "aggregate" or "distinct", cursor is present,
695+
* snapshot is enabled for the session, and snapshot_time has not
696+
* already been set, try to find atClusterTime in cursor field to set
697+
* snapshot_time. */
698+
bson_iter_recurse (&iter, &cursor_iter);
699+
700+
while (bson_iter_next (&cursor_iter)) {
701+
/* If atClusterTime is in cursor and is a valid timestamp, use it to
702+
* set snapshot_time. */
703+
if (!strcmp (bson_iter_key (&cursor_iter), "atClusterTime") &&
704+
BSON_ITER_HOLDS_TIMESTAMP (&cursor_iter)) {
705+
bson_iter_timestamp (&cursor_iter, &snapshot_t, &snapshot_i);
706+
_mongoc_client_session_set_snapshot_time (
707+
session, snapshot_t, snapshot_i);
708+
}
709+
}
644710
}
645711
}
646712
}
@@ -751,6 +817,9 @@ _mongoc_client_session_new (mongoc_client_t *client,
751817
session->opts.flags = MONGOC_SESSION_CAUSAL_CONSISTENCY;
752818
}
753819

820+
/* snapshot_time_set is false by default */
821+
_mongoc_client_session_clear_snapshot_time (session);
822+
754823
/* these values are used for testing only. */
755824
session->with_txn_timeout_ms = 0;
756825
session->fail_commit_label = NULL;
@@ -1055,6 +1124,15 @@ mongoc_client_session_start_transaction (mongoc_client_session_t *session,
10551124
GOTO (done);
10561125
}
10571126

1127+
if (mongoc_session_opts_get_snapshot (&session->opts)) {
1128+
bson_set_error (error,
1129+
MONGOC_ERROR_TRANSACTION,
1130+
MONGOC_ERROR_TRANSACTION_INVALID_STATE,
1131+
"Transactions are not supported in snapshot sessions");
1132+
ret = false;
1133+
GOTO (done);
1134+
}
1135+
10581136
if (sd->max_wire_version < 7 ||
10591137
(sd->max_wire_version < 8 && sd->type == MONGOC_SERVER_MONGOS)) {
10601138
bson_set_error (error,
@@ -1491,14 +1569,17 @@ void
14911569
_mongoc_client_session_append_read_concern (const mongoc_client_session_t *cs,
14921570
const bson_t *rc,
14931571
bool is_read_command,
1572+
const char *cmd_name,
14941573
bson_t *cmd)
14951574
{
14961575
const mongoc_read_concern_t *txn_rc;
14971576
mongoc_internal_transaction_state_t txn_state;
14981577
bool user_rc_has_level;
14991578
bool txn_has_level;
15001579
bool has_timestamp;
1580+
bool is_snapshot;
15011581
bool has_level;
1582+
bool is_find_aggregate_distinct;
15021583
bson_t child;
15031584

15041585
ENTRY;
@@ -1512,16 +1593,24 @@ _mongoc_client_session_append_read_concern (const mongoc_client_session_t *cs,
15121593
return;
15131594
}
15141595

1596+
is_find_aggregate_distinct =
1597+
(!strcmp (cmd_name, "find") || !strcmp (cmd_name, "aggregate") ||
1598+
!strcmp (cmd_name, "distinct"));
1599+
15151600
has_timestamp =
15161601
(txn_state == MONGOC_INTERNAL_TRANSACTION_STARTING || is_read_command) &&
15171602
mongoc_session_opts_get_causal_consistency (&cs->opts) &&
15181603
cs->operation_timestamp;
1604+
is_snapshot = is_find_aggregate_distinct &&
1605+
mongoc_session_opts_get_snapshot (&cs->opts);
15191606
user_rc_has_level = rc && bson_has_field (rc, "level");
15201607
txn_has_level = txn_state == MONGOC_INTERNAL_TRANSACTION_STARTING &&
15211608
!mongoc_read_concern_is_default (txn_rc);
15221609
has_level = user_rc_has_level || txn_has_level;
15231610

1524-
if (!has_timestamp && !has_level) {
1611+
/* do not append read concern if no causal consistency, snapshot disabled and
1612+
* no read concern is provided. */
1613+
if (!has_timestamp && !is_snapshot && !has_level) {
15251614
return;
15261615
}
15271616

@@ -1531,18 +1620,32 @@ _mongoc_client_session_append_read_concern (const mongoc_client_session_t *cs,
15311620
}
15321621

15331622
if (txn_state == MONGOC_INTERNAL_TRANSACTION_STARTING) {
1534-
/* add transaction's read concern level unless user overrides */
1535-
if (txn_has_level && !user_rc_has_level) {
1623+
/* add transaction's read concern level unless user overrides or snapshot
1624+
* is enabled. */
1625+
if (txn_has_level && !user_rc_has_level && !is_snapshot) {
15361626
bson_append_utf8 (&child, "level", 5, txn_rc->level, -1);
15371627
}
15381628
}
1629+
if (is_snapshot) {
1630+
bson_append_utf8 (
1631+
&child, "level", 5, MONGOC_READ_CONCERN_LEVEL_SNAPSHOT, -1);
1632+
}
15391633

1634+
/* append afterClusterTime if causal consistency and operation_time is set.
1635+
* otherwise append atClusterTime if snapshot enabled and snapshot_time is
1636+
* set. */
15401637
if (has_timestamp) {
15411638
bson_append_timestamp (&child,
15421639
"afterClusterTime",
15431640
16,
15441641
cs->operation_timestamp,
15451642
cs->operation_increment);
1643+
} else if (is_snapshot && cs->snapshot_time_set) {
1644+
bson_append_timestamp (&child,
1645+
"atClusterTime",
1646+
13,
1647+
cs->snapshot_time_timestamp,
1648+
cs->snapshot_time_increment);
15461649
}
15471650

15481651
bson_append_document_end (cmd, &child);
@@ -1621,6 +1724,27 @@ _mongoc_client_session_pin (mongoc_client_session_t *session,
16211724
session->server_id = server_id;
16221725
}
16231726

1727+
void
1728+
_mongoc_client_session_set_snapshot_time (mongoc_client_session_t *session,
1729+
uint32_t t,
1730+
uint32_t i)
1731+
{
1732+
BSON_ASSERT (session);
1733+
BSON_ASSERT (!session->snapshot_time_set);
1734+
1735+
session->snapshot_time_set = true;
1736+
session->snapshot_time_timestamp = t;
1737+
session->snapshot_time_increment = i;
1738+
}
1739+
1740+
void
1741+
_mongoc_client_session_clear_snapshot_time (mongoc_client_session_t *session)
1742+
{
1743+
BSON_ASSERT (session);
1744+
1745+
session->snapshot_time_set = false;
1746+
}
1747+
16241748
bool
16251749
mongoc_client_session_get_dirty (mongoc_client_session_t *session)
16261750
{

src/libmongoc/src/mongoc/mongoc-client-session.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ mongoc_session_opts_set_causal_consistency (mongoc_session_opt_t *opts,
9191
MONGOC_EXPORT (bool)
9292
mongoc_session_opts_get_causal_consistency (const mongoc_session_opt_t *opts);
9393

94+
MONGOC_EXPORT (void)
95+
mongoc_session_opts_set_snapshot (mongoc_session_opt_t *opts, bool snapshot);
96+
97+
MONGOC_EXPORT (bool)
98+
mongoc_session_opts_get_snapshot (const mongoc_session_opt_t *opts);
99+
94100
MONGOC_EXPORT (void)
95101
mongoc_session_opts_set_default_transaction_opts (
96102
mongoc_session_opt_t *opts, const mongoc_transaction_opt_t *txn_opts);

0 commit comments

Comments
 (0)