File: operations.py

package info (click to toggle)
pymongo 4.16.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 23,676 kB
  • sloc: python: 107,763; ansic: 4,597; javascript: 137; makefile: 38; sh: 18
file content (127 lines) | stat: -rw-r--r-- 4,520 bytes parent folder | download | duplicates (2)
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 = []