游标
处理游标故障
游标存在于 MongoDB 服务器上。但是, mongoc_cursor_t
结构为本地进程提供了游标句柄。在客户端上迭代游标时,服务器上可能会出现错误。甚至可能发生网络分区。这意味着应用程序在处理游标故障方面应该具有稳健性。
迭代游标时,应检查是否发生错误。 有关如何稳健地检查错误,请参阅以下示例。
static void print_all_documents (mongoc_collection_t *collection) { mongoc_cursor_t *cursor; const bson_t *doc; bson_error_t error; bson_t query = BSON_INITIALIZER; char *str; cursor = mongoc_collection_find_with_opts (collection, query, NULL, NULL); while (mongoc_cursor_next (cursor, &doc)) { str = bson_as_canonical_extended_json (doc, NULL); printf ("%s\n", str); bson_free (str); } if (mongoc_cursor_error (cursor, &error)) { fprintf (stderr, "Failed to iterate all documents: %s\n", error.message); } mongoc_cursor_destroy (cursor); }
销毁服务器端游标
MongoDB C 驱动程序会在以下情况下自动销毁服务器端游标 mongoc_cursor_destroy 被调用。使用游标后未能调用此函数将导致客户端内存泄漏,并在服务器端消耗额外的内存。如果将游标配置为永不超时,则会导致服务器上的内存泄漏。
可追加游标
可追加游标是即使在返回最终结果后仍保持打开状态的游标。这样,如果将更多文档添加到集合中(即添加到游标的结果集),则可以继续调用 mongoc_cursor_next 以检索这些其他结果。
下面是一个完整的测试案例,演示了可追加游标的用法。
注意
可追加游标仅适用于固定大小集合。
从副本集中跟踪 oplog 的示例。
mongoc-tail.c
static void print_bson (const bson_t *b) { char *str; str = bson_as_canonical_extended_json (b, NULL); fprintf (stdout, "%s\n", str); bson_free (str); } static mongoc_cursor_t * query_collection (mongoc_collection_t *collection, uint32_t last_time) { mongoc_cursor_t *cursor; bson_t query; bson_t gt; bson_t opts; BSON_ASSERT (collection); bson_init (&query); BSON_APPEND_DOCUMENT_BEGIN (&query, "ts", >); BSON_APPEND_TIMESTAMP (>, "$gt", last_time, 0); bson_append_document_end (&query, >); bson_init (&opts); BSON_APPEND_BOOL (&opts, "tailable", true); BSON_APPEND_BOOL (&opts, "awaitData", true); cursor = mongoc_collection_find_with_opts (collection, &query, &opts, NULL); bson_destroy (&query); bson_destroy (&opts); return cursor; } static void tail_collection (mongoc_collection_t *collection) { mongoc_cursor_t *cursor; uint32_t last_time; const bson_t *doc; bson_error_t error; bson_iter_t iter; BSON_ASSERT (collection); last_time = (uint32_t) time (NULL); while (true) { cursor = query_collection (collection, last_time); while (!mongoc_cursor_error (cursor, &error) && mongoc_cursor_more (cursor)) { if (mongoc_cursor_next (cursor, &doc)) { if (bson_iter_init_find (&iter, doc, "ts") && BSON_ITER_HOLDS_TIMESTAMP (&iter)) { bson_iter_timestamp (&iter, &last_time, NULL); } print_bson (doc); } } if (mongoc_cursor_error (cursor, &error)) { if (error.domain == MONGOC_ERROR_SERVER) { fprintf (stderr, "%s\n", error.message); exit (1); } } mongoc_cursor_destroy (cursor); sleep (1); } } int main (int argc, char *argv[]) { mongoc_collection_t *collection; mongoc_client_t *client; mongoc_uri_t *uri; bson_error_t error; if (argc != 2) { fprintf (stderr, "usage: %s MONGO_URI\n", argv[0]); return EXIT_FAILURE; } mongoc_init (); uri = mongoc_uri_new_with_error (argv[1], &error); if (!uri) { fprintf (stderr, "failed to parse URI: %s\n" "error message: %s\n", argv[1], error.message); return EXIT_FAILURE; } client = mongoc_client_new_from_uri (uri); if (!client) { return EXIT_FAILURE; } mongoc_client_set_error_api (client, 2); collection = mongoc_client_get_collection (client, "local", "oplog.rs"); tail_collection (collection); mongoc_collection_destroy (collection); mongoc_uri_destroy (uri); mongoc_client_destroy (client); return EXIT_SUCCESS; }
让我们针对副本集编译并运行此示例,以查看进行的更新。
$ gcc -Wall -o mongoc-tail mongoc-tail.c $(pkg-config --cflags --libs libmongoc-1.0) $ ./mongoc-tail mongodb://example.com/?replicaSet=myReplSet { "h" : -8458503739429355503, "ns" : "test.test", "o" : { "_id" : { "$oid" : "5372ab0a25164be923d10d50" } }, "op" : "i", "ts" : { "$timestamp" : { "i" : 1, "t" : 1400023818 } }, "v" : 2 }
该输出行是从 mongo Shell 在副本集上执行db.test.insert({})
的示例。