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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//! The allowances one **request** holds, lent to every read it issues.
//!
//! A ceiling written as a per-request bound and enforced per read is not the control
//! the operator declared. `[validation] max_response_bytes` was that until `a09bc0dae`:
//! the REST `?select=` representation answers one request with the parent read plus one
//! sub-read per parent row per level, each read built a budget of its own, and the
//! ceiling therefore bounded every read and bounded their sum never.
//!
//! `[security.cost_budget] per_request_max` is the same control with the same defect —
//! its name says *per request* and `resolve_direct_read` scores each read alone. This
//! module holds the running total that fixes it ([`CostBudget`]) and the object a
//! transport builds once per request and lends to each of its reads
//! ([`RequestBudget`]).
//!
//! # Why the two allowances travel as one object
//!
//! Because the read chokepoint already takes one of them, and a second
//! `Option<&_>` beside it would be positional. `RowReadPlan` records the same
//! preference one file over — "a named field is harder to pass in the wrong position
//! than the fourth element of a tuple" — and the composed `LATERAL` statement has more
//! per-request state to come. A third allowance is a field here, not a fifth argument
//! there.
//!
//! The members stay `Option`, because "the operator declared no ceiling" is a shape
//! the code has to handle either way, and it is not the same as a ceiling of zero.
//!
//! # Why a cost aggregate is accumulated rather than scored
//!
//! A GraphQL document is scored whole, nesting included (`estimate_query_cost`),
//! because the document *states* its shape before anything runs. A `?select=` does
//! not: how many sub-reads it issues is one per parent row per level, and the parent
//! rows are not back yet. There is no point at which the request's total cost could be
//! computed in advance.
//!
//! So it is accumulated — charged by each read as that read resolves. The property the
//! gate exists for survives: the charge happens in `resolve_direct_read`, *before* the
//! statement is sent, so the read that crosses the ceiling is refused without reaching
//! the database. What changes is only which reads count against it.
//!
//! This is also a control the composed statement keeps. Under `LATERAL` the embedded
//! levels arrive in **one** read, and a running total charged once still bounds the
//! same work under the same ceiling. The arithmetic for scoring a nested projection is
//! the composition's to supply; the budget that score charges is this one, unchanged.
use ;
use ResponseBudget;
use crate;
/// A running total of estimated operation cost, against
/// `[security.cost_budget] per_request_max`.
///
/// Charged before each read runs, by whoever resolves it. **Whoever holds one decides
/// what the ceiling bounds**, the same rule [`ResponseBudget`] states:
///
/// * a read that *is* the whole request resolves one of its own, so the ceiling bounds that read —
/// which is every direct read that is not part of a fan-out, and is the behaviour this type
/// preserves rather than changes;
/// * a request whose transport issues **several** reads on its behalf lends one budget to all of
/// them, so the ceiling bounds the request, which is what `per_request_max` has always said it
/// does.
///
/// `used` is atomic and [`charge`](Self::charge) takes `&self` for that second case, so
/// sharing a budget is holding it by reference rather than a second type with a second
/// copy of the rule.
///
/// Deliberately **not** `Clone`, for the reason [`ResponseBudget`] is not: a clone is an
/// unshared copy with its own zeroed total, which is exactly the defect above, and it
/// would compile in silence.
/// Everything **one request** is allowed to spend, held by the transport that answers
/// it and borrowed by each read it issues.
///
/// Built by `Executor::request_budget` so the ceilings are read from the compiled
/// configuration in the one place that owns them, and a transport cannot supply a
/// budget with ceilings of its own choosing.
///
/// Not `Clone`, because neither member is, and for the same reason.