1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
# Copyright 2015 MongoDB, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"),;
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
from collections import namedtuple
try:
from mockupdb import OpMsgReply, OpReply
_HAVE_MOCKUPDB = True
except ImportError:
_HAVE_MOCKUPDB = False
from pymongo import ReadPreference
__all__ = ["operations", "upgrades"]
Operation = namedtuple("Operation", ["name", "function", "reply", "op_type", "not_master"])
"""Client operations on MongoDB.
Each has a human-readable name, a function that actually executes a test, and
a type that maps to one of the types in the Server Selection Spec:
'may-use-secondary', 'must-use-primary', etc.
The special type 'always-use-secondary' applies to an operation with an explicit
read mode, like the operation "command('c', read_preference=SECONDARY)".
The not-master response is how a secondary responds to a must-use-primary op,
or how a recovering member responds to a may-use-secondary op.
Example uses:
We can use "find_one" to validate that the SlaveOk bit is set when querying a
standalone, even with mode PRIMARY, but that it isn't set when sent to a mongos
with mode PRIMARY. Or it can validate that "$readPreference" is included in
mongos queries except with mode PRIMARY or SECONDARY_PREFERRED (PYTHON-865).
We can use "options_old" and "options_new" to test that the driver queries an
old server's system.namespaces collection, but uses the listCollections command
on a new server (PYTHON-857).
"secondary command" is good to test that the client can direct reads to
secondaries in a replica set, or select a mongos for secondary reads in a
sharded cluster (PYTHON-868).
"""
if _HAVE_MOCKUPDB:
not_master_reply = OpMsgReply(ok=0, errmsg="not master")
operations = [
Operation(
"find_one",
lambda client: client.db.collection.find_one(),
reply={"cursor": {"id": 0, "firstBatch": []}},
op_type="may-use-secondary",
not_master=not_master_reply,
),
Operation(
"count_documents",
lambda client: client.db.collection.count_documents({}),
reply={"n": 1},
op_type="may-use-secondary",
not_master=not_master_reply,
),
Operation(
"estimated_document_count",
lambda client: client.db.collection.estimated_document_count(),
reply={"n": 1},
op_type="may-use-secondary",
not_master=not_master_reply,
),
Operation(
"aggregate",
lambda client: client.db.collection.aggregate([]),
reply={"cursor": {"id": 0, "firstBatch": []}},
op_type="may-use-secondary",
not_master=not_master_reply,
),
Operation(
"options",
lambda client: client.db.collection.options(),
reply={"cursor": {"id": 0, "firstBatch": []}},
op_type="must-use-primary",
not_master=not_master_reply,
),
Operation(
"command",
lambda client: client.db.command("foo"),
reply={"ok": 1},
op_type="must-use-primary", # Ignores client's read preference.
not_master=not_master_reply,
),
Operation(
"secondary command",
lambda client: client.db.command("foo", read_preference=ReadPreference.SECONDARY),
reply={"ok": 1},
op_type="always-use-secondary",
not_master=OpReply(ok=0, errmsg="node is recovering"),
),
Operation(
"listIndexes",
lambda client: client.db.collection.index_information(),
reply={"cursor": {"id": 0, "firstBatch": []}},
op_type="must-use-primary",
not_master=not_master_reply,
),
]
else:
operations = []
_ops_by_name = {op.name: op for op in operations}
Upgrade = namedtuple("Upgrade", ["name", "function", "old", "new", "wire_version"])
upgrades = []
|