|
4 | 4 | from gzip import GzipFile
|
5 | 5 | from bson import BSON, decode_file_iter
|
6 | 6 | from bson.codec_options import CodecOptions
|
| 7 | +from time import time |
7 | 8 |
|
8 | 9 | from mongodb_consistent_backup.Errors import OperationError
|
9 | 10 |
|
10 | 11 |
|
11 | 12 | class Oplog:
|
12 |
| - def __init__(self, oplog_file, do_gzip=False, file_mode="r"): |
| 13 | + def __init__(self, oplog_file, do_gzip=False, file_mode="r", flush_docs=100, flush_secs=1): |
13 | 14 | self.oplog_file = oplog_file
|
14 | 15 | self.do_gzip = do_gzip
|
15 | 16 | self.file_mode = file_mode
|
| 17 | + self.flush_docs = flush_docs |
| 18 | + self.flush_secs = flush_secs |
16 | 19 |
|
17 | 20 | self._count = 0
|
18 | 21 | self._first_ts = None
|
19 | 22 | self._last_ts = None
|
20 | 23 | self._oplog = None
|
21 | 24 |
|
| 25 | + self._last_flush_time = time() |
| 26 | + self._writes_unflushed = 0 |
| 27 | + |
22 | 28 | self.open()
|
23 | 29 |
|
24 | 30 | def handle(self):
|
@@ -56,23 +62,50 @@ def load(self):
|
56 | 62 | logging.fatal("Error reading oplog file %s! Error: %s" % (self.oplog_file, e))
|
57 | 63 | raise OperationError(e)
|
58 | 64 |
|
59 |
| - def add(self, doc): |
| 65 | + def add(self, doc, autoflush=True): |
60 | 66 | try:
|
61 | 67 | self._oplog.write(BSON.encode(doc))
|
62 |
| - self._count += 1 |
| 68 | + self._writes_unflushed += 1 |
| 69 | + self._count += 1 |
63 | 70 | if not self._first_ts:
|
64 | 71 | self._first_ts = doc['ts']
|
65 | 72 | self._last_ts = doc['ts']
|
| 73 | + if autoflush: |
| 74 | + self.autoflush() |
66 | 75 | except Exception, e:
|
67 | 76 | logging.fatal("Cannot write to oplog file %s! Error: %s" % (self.oplog_file, e))
|
68 | 77 | raise OperationError(e)
|
69 | 78 |
|
| 79 | + def secs_since_flush(self): |
| 80 | + return time() - self._last_flush_time |
| 81 | + |
| 82 | + def do_flush(self): |
| 83 | + if self._writes_unflushed > self.flush_docs: |
| 84 | + return True |
| 85 | + elif self.secs_since_flush() > self.flush_secs: |
| 86 | + return True |
| 87 | + return False |
| 88 | + |
70 | 89 | def flush(self):
|
71 | 90 | if self._oplog:
|
72 | 91 | return self._oplog.flush()
|
73 | 92 |
|
| 93 | + def fsync(self): |
| 94 | + if self._oplog: |
| 95 | + # https://docs.python.org/2/library/os.html#os.fsync |
| 96 | + self._oplog.flush() |
| 97 | + self._last_flush_time = time() |
| 98 | + self._writes_unflushed = 0 |
| 99 | + return os.fsync(self._oplog.fileno()) |
| 100 | + |
| 101 | + def autoflush(self): |
| 102 | + if self._oplog and self.do_flush(): |
| 103 | + logging.debug("Fsyncing %s (secs_since=%.2f, changes=%i, ts=%s)" % (self.oplog_file, self.secs_since_flush(), self._writes_unflushed, self.last_ts())) |
| 104 | + return self.fsync() |
| 105 | + |
74 | 106 | def close(self):
|
75 | 107 | if self._oplog:
|
| 108 | + self.fsync() |
76 | 109 | return self._oplog.close()
|
77 | 110 |
|
78 | 111 | def count(self):
|
|
0 commit comments