12
12
// See the License for the specific language governing permissions and
13
13
// limitations under the License.
14
14
15
+ #include < chrono>
15
16
#include < iostream>
17
+ #include < thread>
16
18
#include < vector>
17
19
18
20
#include < bsoncxx/builder/basic/array.hpp>
22
24
#include < bsoncxx/stdx/string_view.hpp>
23
25
#include < bsoncxx/types.hpp>
24
26
#include < mongocxx/client.hpp>
27
+ #include < mongocxx/exception/operation_exception.hpp>
25
28
#include < mongocxx/instance.hpp>
26
29
#include < mongocxx/options/find.hpp>
27
30
#include < mongocxx/uri.hpp>
@@ -54,6 +57,19 @@ void check_has_no_field(const T& document, const char* field, int example_no) {
54
57
check_field (document, field, false , example_no);
55
58
}
56
59
60
+ static bsoncxx::document::value get_is_master (const mongocxx::client& client) {
61
+ using bsoncxx::builder::basic::kvp;
62
+ using bsoncxx::builder::basic::make_document;
63
+
64
+ static auto reply = client[" admin" ].run_command (make_document (kvp (" isMaster" , 1 )));
65
+ return reply;
66
+ }
67
+
68
+ static bool is_replica_set (const mongocxx::client& client) {
69
+ auto reply = get_is_master (client);
70
+ return static_cast <bool >(reply.view ()[" setName" ]);
71
+ }
72
+
57
73
void insert_examples (mongocxx::database db) {
58
74
db[" inventory" ].drop ();
59
75
@@ -1180,6 +1196,178 @@ void delete_examples(mongocxx::database db) {
1180
1196
}
1181
1197
}
1182
1198
1199
+ static bool is_snapshot_ready (mongocxx::client& client, mongocxx::collection& collection) {
1200
+ auto opts = mongocxx::options::client_session{};
1201
+ opts.snapshot (true );
1202
+
1203
+ auto session = client.start_session (opts);
1204
+ try {
1205
+ auto cursor = collection.aggregate (session, {});
1206
+ for (const auto & it : cursor) {
1207
+ (void )it;
1208
+ break ;
1209
+ }
1210
+ } catch (const mongocxx::operation_exception& e) {
1211
+ if (e.code ().value () == 246 ) { // snapshot unavailable
1212
+ return false ;
1213
+ }
1214
+ throw ;
1215
+ }
1216
+ return true ;
1217
+ }
1218
+
1219
+ // Seed the pets database and wait for the snapshot to become available.
1220
+ // This follows the pattern from the Python driver as seen below:
1221
+ // https://github.com/mongodb/mongo-python-driver/commit/e325b24b78e431cb889c5902d00b8f4af2c700c3#diff-c5d782e261f04fca18024ab18c3ed38fb45ede24cde4f9092e012f6fcbbe0df5R1368
1222
+ static void wait_for_snapshot_ready (mongocxx::client& client,
1223
+ std::vector<mongocxx::collection> collections) {
1224
+ size_t sleep_time = 1 ;
1225
+
1226
+ for (;;) {
1227
+ bool is_ready = true ;
1228
+ for (auto & collection : collections) {
1229
+ if (!is_snapshot_ready (client, collection)) {
1230
+ is_ready = false ;
1231
+ break ; // inner
1232
+ }
1233
+ }
1234
+ if (is_ready) {
1235
+ break ; // outer
1236
+ } else {
1237
+ std::this_thread::sleep_for (std::chrono::seconds (sleep_time++));
1238
+ }
1239
+ }
1240
+ }
1241
+
1242
+ static void setup_pets (mongocxx::client& client) {
1243
+ using namespace mongocxx ;
1244
+ using bsoncxx::builder::basic::kvp;
1245
+ using bsoncxx::builder::basic::make_document;
1246
+
1247
+ auto db = client[" pets" ];
1248
+ db.drop ();
1249
+ db[" cats" ].insert_one (make_document (kvp (" adoptable" , true )));
1250
+ db[" dogs" ].insert_one (make_document (kvp (" adoptable" , true )));
1251
+ db[" dogs" ].insert_one (make_document (kvp (" adoptable" , false )));
1252
+ wait_for_snapshot_ready (client, {db[" cats" ], db[" dogs" ]});
1253
+ }
1254
+
1255
+ static void snapshot_example1 (mongocxx::client& client) {
1256
+ setup_pets (client);
1257
+
1258
+ // Start Snapshot Query Example 1
1259
+ using namespace mongocxx ;
1260
+ using bsoncxx::builder::basic::kvp;
1261
+ using bsoncxx::builder::basic::make_document;
1262
+
1263
+ auto db = client[" pets" ];
1264
+
1265
+ int64_t adoptable_pets_count = 0 ;
1266
+
1267
+ auto opts = mongocxx::options::client_session{};
1268
+ opts.snapshot (true );
1269
+ auto session = client.start_session (opts);
1270
+
1271
+ {
1272
+ pipeline p;
1273
+
1274
+ p.match (make_document (kvp (" adoptable" , true ))).count (" adoptableCatsCount" );
1275
+ auto cursor = db[" cats" ].aggregate (session, p);
1276
+
1277
+ for (auto doc : cursor) {
1278
+ adoptable_pets_count += doc.find (" adoptableCatsCount" )->get_int32 ();
1279
+ }
1280
+ }
1281
+
1282
+ {
1283
+ pipeline p;
1284
+
1285
+ p.match (make_document (kvp (" adoptable" , true ))).count (" adoptableDogsCount" );
1286
+ auto cursor = db[" dogs" ].aggregate (session, p);
1287
+
1288
+ for (auto doc : cursor) {
1289
+ adoptable_pets_count += doc.find (" adoptableDogsCount" )->get_int32 ();
1290
+ }
1291
+ }
1292
+
1293
+ // End Snapshot Query Example 1
1294
+
1295
+ if (adoptable_pets_count != 2 ) {
1296
+ throw std::logic_error (
1297
+ " wrong number of adoptable pets in Snapshot Query Example 1, expecting 2 got: " +
1298
+ std::to_string (adoptable_pets_count));
1299
+ }
1300
+ }
1301
+
1302
+ static void setup_retail (mongocxx::client& client) {
1303
+ using bsoncxx::builder::basic::kvp;
1304
+ using bsoncxx::builder::basic::make_document;
1305
+ using bsoncxx::types::b_date;
1306
+ using std::chrono::system_clock;
1307
+
1308
+ auto db = client[" retail" ];
1309
+ db.drop ();
1310
+ b_date sales_date{system_clock::now ()};
1311
+ db[" sales" ].insert_one (
1312
+ make_document (kvp (" shoeType" , " boot" ), kvp (" price" , 30 ), kvp (" saleDate" , sales_date)));
1313
+ wait_for_snapshot_ready (client, {db[" sales" ]});
1314
+ }
1315
+
1316
+ static void snapshot_example2 (mongocxx::client& client) {
1317
+ setup_retail (client);
1318
+
1319
+ // Start Snapshot Query Example 2
1320
+ using namespace mongocxx ;
1321
+ using bsoncxx::builder::basic::kvp;
1322
+ using bsoncxx::builder::basic::make_array;
1323
+ using bsoncxx::builder::basic::make_document;
1324
+
1325
+ auto opts = mongocxx::options::client_session{};
1326
+ opts.snapshot (true );
1327
+ auto session = client.start_session (opts);
1328
+
1329
+ auto db = client[" retail" ];
1330
+
1331
+ pipeline p;
1332
+
1333
+ p.match (make_document (kvp (" $expr" ,
1334
+ make_document (kvp (" $gt" ,
1335
+ make_array (" $saleDate" ,
1336
+ make_document (kvp (" startDate" , " $$NOW" ),
1337
+ kvp (" unit" , " day" ),
1338
+ kvp (" amount" , 1 ))))))))
1339
+ .count (" totalDailySales" );
1340
+
1341
+ auto cursor = db[" sales" ].aggregate (session, p);
1342
+
1343
+ auto doc = *cursor.begin ();
1344
+ auto total_daily_sales = doc.find (" totalDailySales" )->get_int32 ();
1345
+
1346
+ // End Snapshot Query Example 2
1347
+ if (total_daily_sales != 1 ) {
1348
+ throw std::logic_error (" wrong number of total sales in example 60, expecting 1 got: " +
1349
+ std::to_string (total_daily_sales));
1350
+ }
1351
+ }
1352
+
1353
+ static bool version_at_least (mongocxx::v_noabi::database& db, int minimum_major) {
1354
+ using bsoncxx::builder::basic::kvp;
1355
+ using bsoncxx::builder::basic::make_document;
1356
+
1357
+ auto resp = db.run_command (make_document (kvp (" buildInfo" , 1 )));
1358
+ auto version = resp.find (" version" )->get_string ().value ;
1359
+ std::string major_string;
1360
+ for (auto i : version) {
1361
+ if (i == ' .' ) {
1362
+ break ;
1363
+ }
1364
+ major_string += i;
1365
+ }
1366
+ int server_major = std::stoi (major_string);
1367
+
1368
+ return server_major >= minimum_major;
1369
+ }
1370
+
1183
1371
int main () {
1184
1372
// The mongocxx::instance constructor and destructor initialize and shut down the driver,
1185
1373
// respectively. Therefore, a mongocxx::instance must be created before using the driver and
@@ -1199,6 +1387,10 @@ int main() {
1199
1387
projection_examples (db);
1200
1388
update_examples (db);
1201
1389
delete_examples (db);
1390
+ if (is_replica_set (conn) && version_at_least (db, 5 )) {
1391
+ snapshot_example1 (conn);
1392
+ snapshot_example2 (conn);
1393
+ }
1202
1394
} catch (const std::logic_error& e) {
1203
1395
std::cerr << e.what () << std::endl;
1204
1396
return EXIT_FAILURE;
0 commit comments