Skip to content

Commit cc8bec5

Browse files
authoredOct 14, 2024
minor: add code walkthrough notes (#1223)
1 parent ef0893f commit cc8bec5

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed
 

Diff for: ‎docs/tour.md

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# A Guided Tour of the Rust Driver Codebase
2+
3+
These are notes intended to accompany an informal walkthrough of key parts of the driver's code; they may be useful on their own but are not intended to be comprehensive or prescriptive.
4+
5+
## Constructing the Client
6+
7+
[src/client.rs](../src/client.rs)
8+
9+
Entry point of the API. The first thing most users will interact with.
10+
11+
* It's just a wrapper around an `Arc`-wrapped internal struct so users can cheaply `clone` it for convenient storage, passing to spawned tasks, etc.
12+
* (actually a `TrackingArc`, which if a compile-time flag is turned on will track where clones are constructed for debugging)
13+
* Notable internal bits:
14+
* `topology`: tracks the servers we're connected to and maintains a pool of connections for each server.
15+
* `options`: usually parsed from user-provided URI
16+
* `Client` can be constructed from:
17+
* A URI string. By far the most common.
18+
* An options object directly. Power user tool.
19+
* A builder if the user needs to enable in-use encryption.
20+
* Events!
21+
* Three different kinds:
22+
* `Command`: "we told the server to do something"
23+
* `CMAP`: "something happened with an open connection"
24+
* `SDAM`: "something happened with a monitored server"
25+
* plus logging (via `tracing`)!
26+
* `pub fn database`: gateway to the rest of the public API
27+
* `register_async_drop`: Rust doesn't have `async drop`, so we built our own.
28+
* `select_server`: apply criteria to topology, get server (which has connection pool)
29+
30+
## Doing Stuff to Data
31+
32+
[src/db.rs](../src/db.rs)
33+
34+
Gotta go through `Database` to do just about anything. Primarily users will be getting handles to `Collection`s but there are a bunch of bulk actions that can be done directly.
35+
36+
* Like `Client`, it's just an `Arc` around an inner struct so users can cheaply `clone` it and pass it around.
37+
* The inner struct is much lighter: a handle on the parent `Client`, a string name, and some options.
38+
* `new` isn't public. Have to get it from `Client`.
39+
* Can get a `Collection` from it, but there aren't any data operations in here, leading to...
40+
41+
## Anatomy of an Action
42+
43+
[src/action/aggregate.rs](../src/action/aggregate.rs)
44+
45+
*Actions* are the leaves of the public API. They allow for fluent minimal-boilerplate option setting with Rustic type safety and minimal overhead; the drawback is that the internals are a little gnarly.
46+
47+
A usage example:
48+
49+
```rust
50+
let cursor = db
51+
.aggregate([doc!{ ... }]) // [1]
52+
.bypass_document_validation(true) // [2]
53+
.session(s) // [3]
54+
.await?; // [4]
55+
```
56+
57+
Breaking down what's happening here:
58+
59+
1. This constructs the transient `Aggregate` type. Typically users will never deal with this type directly; it'll be constructed and consumed in the same method call chain. The transient action types can be thought of as _pending_ actions; they contain a reference to the target of the call, the parameters, the options, and the session.
60+
2. This sets an option in the contained options object via the `option_setters!` proc macro, which also generates helpful doc links.
61+
3. This sets the pending action to use an explicit session. Note that this can only be done if the action was using an implicit session; Rust lets us enforce at compile-time that you can't call `.session` twice :) We track this at the type level because it changes the type of the returned `Cursor`.
62+
4. The `action_impl` proc macro will generate an `IntoFuture` impl for `Aggregate`, so when `await` is called it will be converted into a call to `execute`.
63+
64+
With all that, the body of `execute` is pretty small - it constructs an `Aggregate` _operation_ and hands that to the client's `execute_cursor_operation`. This pairing between action and operation is very common: _action_ is the public API and _operation_ is the command sent to the server.
65+
66+
## Observing an Operation
67+
68+
[src/operation/insert.rs](../src/operation/insert.rs)
69+
70+
Redirecting from aggregate to insert here; aggregate has the cursor machinery on top of operation.
71+
72+
An `Operation` is a command that can be run on a mongodb server, with the bundled knowledge of how to construct the `Command` from the parameters of the `Operation` and how to interpret the `RawCommandResponse` from the server.
73+
74+
The `Operation` trait is split into `Operation` (used for type constraints) and `OperationWithDefaults` (provides default impls and a blanket impl of `Operation`) to allow forwarding types to implement the base `Operation` without new methods silently introducing bugs.
75+
76+
Most `Operation` impls are straightforward: aggregate components into a buffer with minor conditional logic, deserialize the response.
77+
78+
## Examining an Executor
79+
80+
[src/client/executor.rs](../src/client/executor.rs)
81+
82+
This is very much where the sausage is made; it's also very rare for it to need changes.
83+
84+
* `execute_operation` ... throws away some output of `execute_operation_with_details`
85+
* `execute_operation_with_details` does some pre-retry-loop validation, calls into `execute_operation_with_retry`
86+
* `execute_operation_with_retry`
87+
* tracks transaction state
88+
* selects a server
89+
* checks out a connection from the pool of the selected server
90+
* `execute_operation_on_connection`
91+
* handles errors and retries
92+
* `execute_operation_on_connection`
93+
* builds command from operation
94+
* lots of session-handling
95+
* sends wire-format `Message` from `Command`
96+
* does bookkeeping from response
97+
98+
## Future Fields
99+
100+
This is far from comprehensive; most notably, this doesn't cover:
101+
* the internals of `Topology`
102+
* the `bson` crate and our integration with `serde`
103+
* the testing infrastructure
104+

0 commit comments

Comments
 (0)