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
//! Live-traffic shadow mirroring and primary-vs-shadow response diffing
//! (issue #1653).
//!
//! Canary, blue/green, and rolling deploys all answer the same question — *are
//! the aggregate metrics still healthy?* — and all of them route **real**
//! traffic to the new build to answer it. None of them catches the regression
//! that returns `200 OK` with a dropped field, a reordered list, or an
//! off-by-one total, because nothing ever compares two responses to the same
//! request. This module does.
//!
//! ```text
//! ┌──────────────────┐
//! client ──request──▶ │ live build │ ──response──▶ client
//! └────────┬─────────┘ │
//! │ (copy) │ (tee)
//! ┌────────▼─────────┐ ┌────▼─────┐
//! │ candidate build │──▶│ differ │──▶ /actuator/shadow
//! └──────────────────┘ └──────────┘ + metrics
//! ```
//!
//! The candidate's response reaches the differ and nothing else. It is never a
//! `Response`, never touches the primary's state, and never delays the client.
//!
//! # Scope of this slice
//!
//! Idempotent (`GET`/`HEAD`) traffic only, against an operator-provided shadow
//! target. Mirroring mutating methods requires virtualizing the candidate's
//! effects — DB writes, outbound HTTP, `#[job]` enqueues — which is the
//! deliberate follow-up slice. The method allowlist is therefore a constant,
//! not a config key.
//!
//! # Wiring
//!
//! Configured through [`ShadowConfig`](crate::shadow::ShadowConfig) (`[shadow]` in
//! `autumn.toml`) and
//! assembled into the ingress stack by the framework router; nothing here needs
//! to be called by hand. See `docs/guide/staged-deploys.md`.
// Private modules behind a curated re-export list, matching
// `crate::middleware`: the module split is an implementation detail, and
// everything below is deliberate public surface. `transport` stays public
// because a third party may want to implement [`ShadowTransport`] against a
// non-HTTP candidate.
pub
pub
pub
pub
pub
pub use ShadowConfig;
pub use ;
pub use ;
pub use ;
pub use ;
pub use HttpShadowTransport;
pub use ;
/// What `{actuator-prefix}/shadow` needs to report on a mirror run: the
/// registry plus the two facts that live in config rather than in it.
///
/// Installed into [`crate::AppState`]'s runtime extension map when the mirror
/// layer is assembled, so a replica with mirroring switched off simply has no
/// handle and the endpoint reports a disabled mirror.