Skip to content
This repository was archived by the owner on Feb 6, 2026. It is now read-only.

Commit 8aa5b0c

Browse files
authored
Merge pull request #7187 from systeminit/jhelwig/eng-3201-implement-intelligent-materializedview-rebuild-system
Implement intelligent materializedview rebuild system on schema changes
2 parents b2f6fba + d4a78e7 commit 8aa5b0c

45 files changed

Lines changed: 4690 additions & 2434 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lib/edda-core/src/api_types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod new_change_set_request;
2+
pub mod rebuild_changed_definitions_request;
23
pub mod rebuild_request;
34
pub mod update_request;
45

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
use acceptable::{
2+
AllVersions,
3+
Container,
4+
CurrentContainer,
5+
IntoContainer,
6+
UpgradeError,
7+
};
8+
use serde::Deserialize;
9+
10+
mod v1;
11+
12+
pub use self::v1::RebuildChangedDefinitionsRequestV1;
13+
14+
#[remain::sorted]
15+
#[derive(AllVersions, CurrentContainer, Clone, Debug, Deserialize, Eq, PartialEq)]
16+
#[serde(rename_all = "camelCase")]
17+
pub enum RebuildChangedDefinitionsRequestAllVersions {
18+
#[acceptable(current)]
19+
V1(RebuildChangedDefinitionsRequestV1),
20+
}
21+
22+
impl IntoContainer for RebuildChangedDefinitionsRequestAllVersions {
23+
type Container = RebuildChangedDefinitionsRequest;
24+
25+
fn into_container(self) -> Result<Self::Container, UpgradeError> {
26+
match self {
27+
Self::V1(inner) => Ok(Self::Container::new(inner)),
28+
}
29+
}
30+
}
31+
32+
#[cfg(test)]
33+
mod test {
34+
use std::{
35+
error::Error,
36+
fmt,
37+
fs::File,
38+
io::{
39+
self,
40+
BufRead as _,
41+
BufReader,
42+
Read as _,
43+
},
44+
path::Path,
45+
};
46+
47+
use serde::{
48+
Serialize,
49+
de::DeserializeOwned,
50+
};
51+
52+
/// Tests that a versioned object will always serialize to the same representation, no matter
53+
/// what future versions or changes to the object.
54+
///
55+
/// NOTE: It is imperative that incremental refactorings do not lead to a version-incompatible
56+
/// or serialize-incompatible change. If a test fails and it's because there is a diff to the
57+
/// commiteed `.snap` file, this should be considered a failed refactoring. The remediation is
58+
/// to *not* update the `.snap` file but rather to fix the code so that the `.snap` format is
59+
/// 100% preserved.
60+
pub fn assert_serialize(name: &str, version: u64, serialize: impl Serialize) {
61+
insta::with_settings!({
62+
snapshot_path => format!("rebuild_changed_definitions_request/snapshots-v{version}"),
63+
prepend_module_to_snapshot => false,
64+
omit_expression => true,
65+
description => concat!(
66+
"\n",
67+
"\n",
68+
"!!!\n",
69+
"!!! System Initiative Developers:\n",
70+
"!!!\n",
71+
"!!! IMPORTANT:\n",
72+
"!!!\n",
73+
"!!! The contents of this snapshot should *never* be modified as it\n",
74+
"!!! represents the serialization of a versioned Rust type. If a tests fails\n",
75+
"!!! with this warning, then something about a Rust type has changed\n",
76+
"!!! the wire serialization of this type and would represent a potential\n",
77+
"!!! production outage or data corruption.\n",
78+
"!!!\n",
79+
"!!! Consider this an erroneous behavioral change of the Rust code and *not*\n",
80+
"!!! an out-of-date snapshot or fixture!\n",
81+
"!!!\n",
82+
"\n",
83+
"\n",
84+
),
85+
}, {
86+
insta::assert_json_snapshot!(name, serialize);
87+
});
88+
}
89+
90+
pub fn assert_deserialize<T>(snapshot_name: &str, version: u64, expected: T)
91+
where
92+
T: fmt::Debug + DeserializeOwned + PartialEq,
93+
{
94+
let actual: T = read_from_snapshot(
95+
snapshot_name,
96+
&format!("rebuild_changed_definitions_request/snapshots-v{version}"),
97+
)
98+
.expect("failed to deserialize from snapshot");
99+
100+
assert_eq!(actual, expected);
101+
}
102+
103+
fn read_from_snapshot<T>(name: &str, path: &str) -> Result<T, Box<dyn Error>>
104+
where
105+
T: fmt::Debug + DeserializeOwned + PartialEq,
106+
{
107+
let glob = format!("{path}/{name}.snap");
108+
109+
let mut maybe_obj_result = None;
110+
insta::glob!(&glob, |path| {
111+
let bytes = read_snapshot_content(path).unwrap();
112+
113+
maybe_obj_result = Some(serde_json::from_slice(&bytes).map_err(Into::into));
114+
});
115+
116+
match maybe_obj_result {
117+
Some(object_result) => object_result,
118+
None => Err(Box::new(io::Error::other(format!(
119+
"snapshot not found: {glob}"
120+
)))),
121+
}
122+
}
123+
124+
// Implementation is adapted from the [`insta::Snapshot::from_file`] function.
125+
//
126+
// See: <https://github.com/mitsuhiko/insta/blob/62bb0a3/insta/src/snapshot.rs#L339-L421>
127+
fn read_snapshot_content(path: &Path) -> Result<Vec<u8>, Box<dyn Error>> {
128+
let mut f = BufReader::new(File::open(path)?);
129+
130+
// Skip through the snapshot metadata, that being the first YAML document, where YAML
131+
// documents are delimited with a `"---"` line
132+
{
133+
let mut buf = String::new();
134+
f.read_line(&mut buf)?;
135+
if buf.trim_end() == "---" {
136+
loop {
137+
let read = f.read_line(&mut buf)?;
138+
if read == 0 {
139+
break;
140+
}
141+
if buf[buf.len() - read..].trim_end() == "---" {
142+
break;
143+
}
144+
}
145+
}
146+
}
147+
148+
let mut buf = Vec::new();
149+
f.read_to_end(&mut buf)?;
150+
151+
Ok(buf)
152+
}
153+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
source: lib/edda-core/src/api_types/rebuild_changed_definitions_request.rs
3+
description: "\n\n!!!\n!!! System Initiative Developers:\n!!!\n!!! IMPORTANT:\n!!!\n!!! The contents of this snapshot should *never* be modified as it\n!!! represents the serialization of a versioned Rust type. If a tests fails\n!!! with this warning, then something about a Rust type has changed\n!!! the wire serialization of this type and would represent a potential\n!!! production outage or data corruption.\n!!!\n!!! Consider this an erroneous behavioral change of the Rust code and *not*\n!!! an out-of-date snapshot or fixture!\n!!!\n\n\n"
4+
---
5+
{
6+
"id": "01JQCVVDHXYX6S9YCV773R13MG"
7+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use acceptable::{
2+
RequestId,
3+
Versioned,
4+
};
5+
use serde::{
6+
Deserialize,
7+
Serialize,
8+
};
9+
10+
#[derive(Clone, Debug, Deserialize, Eq, Serialize, PartialEq, Versioned)]
11+
#[serde(rename_all = "camelCase")]
12+
#[acceptable(version = 1)]
13+
// NOTE: **do not modify this datatype--it represents a historically stable, versioned request**
14+
pub struct RebuildChangedDefinitionsRequestV1 {
15+
pub id: RequestId,
16+
}
17+
18+
#[cfg(test)]
19+
mod tests {
20+
use super::{
21+
super::test::*,
22+
*,
23+
};
24+
25+
const SNAPSHOT_NAME: &str = "serialized";
26+
const VERSION: u64 = 1;
27+
28+
fn msg() -> RebuildChangedDefinitionsRequestV1 {
29+
RebuildChangedDefinitionsRequestV1 {
30+
id: "01JQCVVDHXYX6S9YCV773R13MG".parse().unwrap(),
31+
}
32+
}
33+
34+
#[test]
35+
fn serialize() {
36+
assert_serialize(SNAPSHOT_NAME, VERSION, msg());
37+
}
38+
39+
#[test]
40+
fn deserialize() {
41+
assert_deserialize(SNAPSHOT_NAME, VERSION, msg());
42+
}
43+
}

0 commit comments

Comments
 (0)