pymongo bugfix后记
有網(wǎng)友反饋py-mongo-sync同步異常,檢查發(fā)現(xiàn)curosr[0]取查詢結(jié)果第一個文檔時報錯”no such item for Cursor instance”。
這里的邏輯是,根據(jù)timestamp查詢oplog起始位置,cursor類型是TAILABLE,然后取出第一條oplog,驗證timestamp是否一致。
對方mongo版本是v3.4,同步工具在v3.2及更早版本的MongoDB上還沒出過類似問題。
使用pymongo 3.5.1(最新版本)分別用v3.2和v3.4跑同步測試,在query結(jié)果非空的情況下,v3.2正常,v3.4報錯,懷疑是不是v3.4做了什么改動,導(dǎo)致dirver不兼容。
進(jìn)一步排查,cursor[0]讀取文檔,db端返回了一個錯誤,意思說tailable和singleBatch兩個選項沖突。
{'number_returned': 1, 'data': [SON([(u'ok', 0.0), (u'errmsg', u"cannot use tailable option with the 'singleBatch' option"), (u'code', 2), (u'codeName', u'BadValue')])], 'starting_from': 0, 'cursor_id': 0}
cursor.__getitem__(self, index)方法,如果取單個文檔,首先對當(dāng)前cursor進(jìn)行clone,將limit設(shè)置為-1(db返回一條文檔并關(guān)閉cursor,可參考ntoreturn vs batchSize),表示只讀取一條文檔。
577 if isinstance(index, integer_types):
578 if index < 0:
579 raise IndexError("Cursor instances do not support negative "
580 "indices")
581 clone = self.clone()
582 clone.skip(index + self.__skip)
583 clone.limit(-1) # use a hard limit
584 for doc in clone: 大專欄 pymongo bugfix后記
585 return doc
586 raise IndexError("no such item for Cursor instance")
587 raise TypeError("index %r cannot be applied to Cursor "
588 "instances" % index)
cursor類型是TAILABLE或TAILABLE_AWAIT,所以query的tailable選項為True;
cursor.limit設(shè)置為-1,導(dǎo)致query的singleBatch選項為True;
二者皆為True,引發(fā)沖突。
因為只讀取一條文檔,limit必須為-1,所以需要把TAILABLE和TAILABLE_AWAIT設(shè)置為0,避免選項沖突。
clone.limit(-1) # use a hard limit
clone.__query_flags &= ~CursorType.TAILABLE_AWAIT # PYTHON-1371
至此,問題解決,說說幾點收獲:
盡量不要使用cursor索引訪問文檔,除非是一次性操作
# 以下是原代碼邏輯
coll = self._src_mc['local'].get_collection('oplog.rs', codec_options=bson.codec_options.CodecOptions(document_class=bson.son.SON))
cursor = coll.find({'ts': {'$gte': oplog_start}}, cursor_type=pymongo.cursor.CursorType.TAILABLE_AWAIT, no_cursor_timeout=True)
if cursor[0]['ts'] == oplog_start: # 這里對原始cursor進(jìn)行clone,執(zhí)行查詢(第一次),然后關(guān)閉cursor_cloned
# 之前在跑同步時,時間戳一致,但此處可能仍有時間長短不一的等待,始終不明所以
# 原因是下面在調(diào)用cursor.next()時會重新執(zhí)行查詢
while True:
oplog = cursor.next() # 這里使用原始cursor,執(zhí)行查詢(第二次)
# handle oplog
提交PR前,確保本地跑通test case,當(dāng)時僅考慮到TAILABLE的情況,而忽略了TAILABLE_AWAIT
掌握PR流程:提交PR后,如果review未通過,需要修改代碼,在你的local分支下繼續(xù)commit并push到GitHub,新commit會自動追加到該PR,最后由項目維護(hù)者完成merge,可以參考collaborating-with-issues-and-pull-requests
最后,很高興PR能被官方merge :)
總結(jié)
以上是生活随笔為你收集整理的pymongo bugfix后记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 非常完整的coco screator s
- 下一篇: python调用企业微信接口发送群聊消息