Skip to content

Commit 38cf613

Browse files
committed
CDRIVER-873 optimize gridfs long seeks
1 parent 80b9d97 commit 38cf613

File tree

2 files changed

+125
-2
lines changed

2 files changed

+125
-2
lines changed

src/mongoc/mongoc-gridfs-file.c

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,44 @@ _mongoc_gridfs_file_flush_page (mongoc_gridfs_file_t *file)
642642
}
643643

644644

645+
/**
646+
* _mongoc_gridfs_file_keep_cursor:
647+
*
648+
* After a seek, decide if the next read should use the current cursor or
649+
* start a new query.
650+
*
651+
* Preconditions:
652+
*
653+
* file has a cursor and cursor range.
654+
*
655+
* Side Effects:
656+
*
657+
* None.
658+
*/
659+
static bool
660+
_mongoc_gridfs_file_keep_cursor (mongoc_gridfs_file_t *file)
661+
{
662+
uint32_t chunk_no;
663+
uint32_t chunks_per_batch;
664+
665+
if (file->n < 0 || file->chunk_size <= 0) {
666+
return false;
667+
}
668+
669+
chunk_no = (uint32_t) file->n;
670+
/* server returns roughly 4 MB batches by default */
671+
chunks_per_batch = (4 * 1024 * 1024) / (uint32_t) file->chunk_size;
672+
673+
return (
674+
/* cursor is on or before the desired chunk */
675+
file->cursor_range[0] <= chunk_no &&
676+
/* chunk_no is before end of file */
677+
chunk_no <= file->cursor_range[1] &&
678+
/* desired chunk is in this batch or next one */
679+
chunk_no < file->cursor_range[0] + 2 * chunks_per_batch);
680+
}
681+
682+
645683
/**
646684
* _mongoc_gridfs_file_refresh_page:
647685
*
@@ -697,8 +735,7 @@ _mongoc_gridfs_file_refresh_page (mongoc_gridfs_file_t *file)
697735
} else {
698736
/* if we have a cursor, but the cursor doesn't have the chunk we're going
699737
* to need, destroy it (we'll grab a new one immediately there after) */
700-
if (file->cursor &&
701-
(file->n < file->cursor_range[0] || file->n > file->cursor_range[1])) {
738+
if (file->cursor && !_mongoc_gridfs_file_keep_cursor (file)) {
702739
mongoc_cursor_destroy (file->cursor);
703740
file->cursor = NULL;
704741
}

tests/test-mongoc-gridfs.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "test-libmongoc.h"
77
#include "mongoc-tests.h"
88
#include "TestSuite.h"
9+
#include "test-conveniences.h"
910

1011

1112
static mongoc_gridfs_t *
@@ -557,6 +558,90 @@ test_stream (void)
557558
mongoc_client_destroy (client);
558559
}
559560

561+
562+
#define ASSERT_TELL(file_, position_) \
563+
ASSERT_CMPUINT64 (mongoc_gridfs_file_tell (file_), ==, position_)
564+
565+
566+
static void
567+
test_long_seek (void)
568+
{
569+
const uint64_t four_mb = 4 * 1024 * 1024;
570+
571+
mongoc_client_t *client;
572+
bson_error_t error;
573+
mongoc_gridfs_t *gridfs;
574+
mongoc_gridfs_file_t *file;
575+
ssize_t r;
576+
mongoc_gridfs_file_opt_t opt = { 0, "filename" };
577+
mongoc_iovec_t iov;
578+
char buf[16 * 1024]; /* nothing special about 16k, just a buffer */
579+
const ssize_t buflen = sizeof (buf);
580+
ssize_t written;
581+
int64_t cursor_id;
582+
int i;
583+
584+
iov.iov_base = buf;
585+
iov.iov_len = sizeof (buf);
586+
587+
client = test_framework_client_new ();
588+
gridfs = get_test_gridfs (client, "long_seek", &error);
589+
ASSERT_OR_PRINT (gridfs, error);
590+
mongoc_gridfs_drop (gridfs, NULL);
591+
file = mongoc_gridfs_create_file (gridfs, &opt);
592+
ASSERT (file);
593+
594+
/* Write 20MB, enough to ensure we need many batches, below */
595+
written = 0;
596+
while (written < 20 * 1024 * 1024) {
597+
r = mongoc_gridfs_file_writev (file, &iov, 1, 0);
598+
ASSERT_CMPLONG (r, ==, buflen);
599+
written += r;
600+
}
601+
602+
/* new file handle */
603+
mongoc_gridfs_file_save (file);
604+
mongoc_gridfs_file_destroy (file);
605+
file = mongoc_gridfs_find_one (gridfs,
606+
tmp_bson ("{'filename': 'filename'}"),
607+
&error);
608+
609+
ASSERT_OR_PRINT (file, error);
610+
611+
/* read the start of the file */
612+
r = mongoc_gridfs_file_readv (file, &iov, 1, sizeof (buf), 0);
613+
ASSERT_CMPLONG (r, ==, buflen);
614+
ASSERT_TELL (file, (uint64_t) buflen);
615+
cursor_id = mongoc_cursor_get_id (file->cursor);
616+
617+
/* seek forward into next batch and read, gridfs advances cursor */
618+
i = mongoc_gridfs_file_seek (file, four_mb, SEEK_CUR);
619+
ASSERT_CMPINT (i, ==, 0);
620+
r = mongoc_gridfs_file_readv (file, &iov, 1, sizeof (buf), 0);
621+
ASSERT_CMPLONG (r, ==, buflen);
622+
ASSERT_TELL (file, four_mb + 2 * buflen);
623+
624+
/* same as the cursor we started with */
625+
ASSERT_CMPINT64 (cursor_id, ==, mongoc_cursor_get_id (file->cursor));
626+
627+
/* seek more than a batch forward, gridfs discards cursor */
628+
i = mongoc_gridfs_file_seek (file, 3 * four_mb, SEEK_CUR);
629+
ASSERT_CMPINT (i, ==, 0);
630+
ASSERT_TELL (file, 4 * four_mb + 2 * buflen);
631+
r = mongoc_gridfs_file_readv (file, &iov, 1, sizeof (buf), 0);
632+
ASSERT_CMPLONG (r, ==, buflen);
633+
ASSERT_TELL (file, 4 * four_mb + 3 * buflen);
634+
635+
/* new cursor, not the one we started with */
636+
ASSERT_CMPINT64 (cursor_id, !=, mongoc_cursor_get_id (file->cursor));
637+
638+
mongoc_gridfs_file_destroy (file);
639+
ASSERT_OR_PRINT (drop_collections (gridfs, &error), error);
640+
mongoc_gridfs_destroy (gridfs);
641+
mongoc_client_destroy (client);
642+
}
643+
644+
560645
static void
561646
test_remove_by_filename (void)
562647
{
@@ -616,5 +701,6 @@ test_gridfs_install (TestSuite *suite)
616701
TestSuite_Add (suite, "/GridFS/stream", test_stream);
617702
TestSuite_Add (suite, "/GridFS/remove", test_remove);
618703
TestSuite_Add (suite, "/GridFS/write", test_write);
704+
TestSuite_Add (suite, "/GridFS/test_long_seek", test_long_seek);
619705
TestSuite_Add (suite, "/GridFS/remove_by_filename", test_remove_by_filename);
620706
}

0 commit comments

Comments
 (0)