mongodb/
hello.rs

1use std::time::Duration;
2
3use bson::{rawdoc, RawDocumentBuf};
4use serde::{Deserialize, Serialize};
5use tokio::sync::broadcast;
6
7use crate::{
8    bson::{doc, oid::ObjectId, DateTime, Document, Timestamp},
9    client::{
10        options::{ServerAddress, ServerApi},
11        ClusterTime,
12    },
13    cmap::{Command, Connection},
14    error::Result,
15    sdam::{ServerType, TopologyVersion},
16    selection_criteria::TagSet,
17};
18
19/// The legacy version of the `hello` command which was deprecated in 5.0.
20/// To limit usages of the legacy name in the codebase, this constant should be used
21/// wherever possible.
22pub(crate) const LEGACY_HELLO_COMMAND_NAME: &str = "isMaster";
23pub(crate) const LEGACY_HELLO_COMMAND_NAME_LOWERCASE: &str = "ismaster";
24
25#[derive(Debug, Clone, Copy)]
26pub(crate) struct AwaitableHelloOptions {
27    pub(crate) topology_version: TopologyVersion,
28    pub(crate) max_await_time: Duration,
29}
30
31/// Construct a hello or legacy hello command, depending on the circumstances.
32///
33/// If an API version is provided or `load_balanced` is true, `hello` will be used.
34/// If the server indicated `helloOk: true`, then `hello` will also be used.
35/// Otherwise, legacy hello will be used, and if it's unknown whether the server supports hello,
36/// the command also will contain `helloOk: true`.
37pub(crate) fn hello_command(
38    server_api: Option<&ServerApi>,
39    load_balanced: Option<bool>,
40    hello_ok: Option<bool>,
41    awaitable_options: Option<AwaitableHelloOptions>,
42) -> Command {
43    let (mut body, command_name) = if server_api.is_some()
44        || matches!(load_balanced, Some(true))
45        || matches!(hello_ok, Some(true))
46    {
47        (rawdoc! { "hello": 1 }, "hello")
48    } else {
49        let mut body = rawdoc! { LEGACY_HELLO_COMMAND_NAME: 1 };
50        if hello_ok.is_none() {
51            body.append("helloOk", true);
52        }
53        (body, LEGACY_HELLO_COMMAND_NAME)
54    };
55
56    if let Some(opts) = awaitable_options {
57        body.append("topologyVersion", opts.topology_version);
58        body.append(
59            "maxAwaitTimeMS",
60            opts.max_await_time
61                .as_millis()
62                .try_into()
63                .unwrap_or(i64::MAX),
64        );
65    }
66
67    let mut command = Command::new(command_name, "admin", body);
68    if let Some(server_api) = server_api {
69        command.set_server_api(server_api);
70    }
71    command.exhaust_allowed = awaitable_options.is_some();
72    command
73}
74
75/// Execute a hello or legacy hello command.
76pub(crate) async fn run_hello(
77    conn: &mut Connection,
78    command: Command,
79    mut cancellation_receiver: Option<broadcast::Receiver<()>>,
80) -> Result<HelloReply> {
81    let response_result = match cancellation_receiver {
82        Some(ref mut cancellation_receiver) => {
83            conn.send_message_with_cancellation(command, cancellation_receiver)
84                .await
85        }
86        None => conn.send_message(command).await,
87    };
88    response_result.and_then(|raw_response| raw_response.into_hello_reply())
89}
90
91#[derive(Debug, Clone, Serialize)]
92pub(crate) struct HelloReply {
93    pub(crate) server_address: ServerAddress,
94    pub(crate) command_response: HelloCommandResponse,
95    pub(crate) raw_command_response: RawDocumentBuf,
96    pub(crate) cluster_time: Option<ClusterTime>,
97}
98
99/// The response to a `hello` command.
100///
101/// See the documentation [here](https://www.mongodb.com/docs/manual/reference/command/hello/) for more details.
102#[derive(Debug, Clone, Default, Deserialize, Serialize)]
103#[serde(rename_all = "camelCase")]
104pub(crate) struct HelloCommandResponse {
105    /// Whether the server is writable. If true, this instance is a primary in a replica set, a
106    /// mongos instance, or a standalone mongod.
107    pub is_writable_primary: Option<bool>,
108
109    #[serde(rename = "ismaster")]
110    /// Legacy name for `is_writable_primary` field.
111    pub is_master: Option<bool>,
112
113    /// Whether or not the server supports using the `hello` command for monitoring instead
114    /// of the legacy hello command.
115    pub hello_ok: Option<bool>,
116
117    /// The list of all hosts.
118    pub hosts: Option<Vec<String>>,
119
120    /// The list of all passives in a replica set.
121    pub passives: Option<Vec<String>>,
122
123    /// The list of all arbiters in a replica set.
124    pub arbiters: Option<Vec<String>>,
125
126    /// An optional message. This contains the value "isdbgrid" when returned from a mongos.
127    pub msg: Option<String>,
128
129    /// The address of the server that returned this `HelloCommandResponse`.
130    pub me: Option<String>,
131
132    #[serde(rename = "compression")]
133    /// The list of compatible compressors that the server returned.
134    pub compressors: Option<Vec<String>>,
135
136    /// The current replica set config version.
137    pub set_version: Option<i32>,
138
139    /// The name of the current replica set.
140    pub set_name: Option<String>,
141
142    /// Whether the server is hidden.
143    pub hidden: Option<bool>,
144
145    /// Whether the server is a secondary.
146    pub secondary: Option<bool>,
147
148    /// Whether the server is an arbiter.
149    pub arbiter_only: Option<bool>,
150
151    #[serde(rename = "isreplicaset")]
152    /// Whether the server is a replica set.
153    pub is_replica_set: Option<bool>,
154
155    /// The time in minutes that a session remains active after its most recent use.
156    pub logical_session_timeout_minutes: Option<i64>,
157
158    /// Optime and date information for the server's most recent write operation.
159    pub last_write: Option<LastWrite>,
160
161    /// The minimum wire version that the server supports.
162    pub min_wire_version: Option<i32>,
163
164    /// The maximum wire version that the server supports.
165    pub max_wire_version: Option<i32>,
166
167    /// User-defined tags for a replica set member.
168    pub tags: Option<TagSet>,
169
170    /// A unique identifier for each election.
171    pub election_id: Option<ObjectId>,
172
173    /// The address of current primary member of the replica set.
174    pub primary: Option<String>,
175
176    /// A list of SASL mechanisms used to create the user's credential(s).
177    pub sasl_supported_mechs: Option<Vec<String>>,
178
179    /// The reply to speculative authentication done in the authentication handshake.
180    pub speculative_authenticate: Option<Document>,
181
182    /// The maximum permitted size of a BSON object in bytes.
183    pub max_bson_object_size: i64,
184
185    /// The maximum number of write operations permitted in a write batch.
186    pub max_write_batch_size: Option<i64>,
187
188    /// If the connection is to a load balancer, the id of the selected backend.
189    pub service_id: Option<ObjectId>,
190
191    /// For internal use.
192    pub topology_version: Option<TopologyVersion>,
193
194    /// The maximum permitted size of a BSON wire protocol message.
195    pub max_message_size_bytes: i32,
196
197    /// The server-generated ID for the connection the "hello" command was run on.
198    /// Present on server versions 4.2+.
199    pub connection_id: Option<i64>,
200}
201
202impl HelloCommandResponse {
203    pub(crate) fn server_type(&self) -> ServerType {
204        if self.msg.as_deref() == Some("isdbgrid") {
205            ServerType::Mongos
206        } else if self.set_name.is_some() {
207            if self.hidden == Some(true) {
208                ServerType::RsOther
209            } else if self.is_writable_primary == Some(true) || self.is_master == Some(true) {
210                ServerType::RsPrimary
211            } else if self.secondary == Some(true) {
212                ServerType::RsSecondary
213            } else if self.arbiter_only == Some(true) {
214                ServerType::RsArbiter
215            } else {
216                ServerType::RsOther
217            }
218        } else if self.is_replica_set == Some(true) {
219            ServerType::RsGhost
220        } else {
221            ServerType::Standalone
222        }
223    }
224}
225
226#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
227#[serde(rename_all = "camelCase")]
228pub(crate) struct LastWrite {
229    pub last_write_date: DateTime,
230}
231
232#[derive(Debug, Clone, PartialEq, Deserialize)]
233pub(crate) struct OpTime {
234    ts: Timestamp,
235    t: i32,
236}