Skip to content

Commit 3066b5c

Browse files
committed
fix: add version rollback check
1 parent 8db9f85 commit 3066b5c

File tree

7 files changed

+110
-7
lines changed

7 files changed

+110
-7
lines changed

Cargo.lock

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ rstest = "0.26.1"
6060
rustls-pemfile = "2.2.0"
6161
rustyline = "15.0.0"
6262
scc = "3.3.2"
63+
semver = "1.0.27"
6364
serde_bare = "0.5.0"
6465
serde_html_form = "0.2.7"
6566
serde_yaml = "0.9.34"

engine/packages/config/src/config/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ pub struct Root {
9595

9696
#[serde(default)]
9797
pub telemetry: Telemetry,
98+
99+
#[serde(default)]
100+
pub allow_version_rollback: bool,
98101
}
99102

100103
impl Default for Root {
@@ -112,6 +115,7 @@ impl Default for Root {
112115
clickhouse: None,
113116
vector_http: None,
114117
telemetry: Default::default(),
118+
allow_version_rollback: false,
115119
}
116120
}
117121
}

engine/packages/engine/Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,28 @@ futures-util.workspace = true
1818
gas.workspace = true
1919
hex.workspace = true
2020
include_dir.workspace = true
21+
indoc.workspace = true
2122
lz4_flex.workspace = true
22-
pegboard-serverless.workspace = true
2323
pegboard-runner.workspace = true
24+
pegboard-serverless.workspace = true
2425
reqwest.workspace = true
2526
rivet-api-peer.workspace = true
2627
rivet-bootstrap.workspace = true
27-
rivet-cache.workspace = true
2828
rivet-cache-purge.workspace = true
29+
rivet-cache.workspace = true
2930
rivet-config.workspace = true
3031
rivet-guard.workspace = true
31-
rivet-tracing-reconfigure.workspace = true
3232
rivet-logs.workspace = true
3333
rivet-pools.workspace = true
3434
rivet-runtime.workspace = true
3535
rivet-service-manager.workspace = true
3636
rivet-telemetry.workspace = true
3737
rivet-term.workspace = true
38+
rivet-tracing-reconfigure.workspace = true
3839
rivet-util.workspace = true
3940
rivet-workflow-worker.workspace = true
4041
rustyline.workspace = true
42+
semver.workspace = true
4143
serde_json.workspace = true
4244
serde_yaml.workspace = true
4345
serde.workspace = true

engine/packages/engine/src/commands/start.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
use std::time::Duration;
22

3-
use anyhow::*;
3+
use anyhow::{Context, Result, anyhow};
44
use clap::Parser;
5+
use indoc::formatdoc;
56
use rivet_service_manager::{CronConfig, RunConfig};
7+
use universaldb::utils::IsolationLevel::*;
8+
9+
use crate::keys;
610

711
// 7 day logs retention
812
const LOGS_RETENTION: Duration = Duration::from_secs(7 * 24 * 60 * 60);
@@ -88,10 +92,52 @@ impl Opts {
8892
.collect::<Vec<_>>()
8993
};
9094

91-
// Start server
9295
let pools = rivet_pools::Pools::new(config.clone()).await?;
96+
97+
verify_engine_version(&config, &pools).await?;
98+
99+
// Start server
93100
rivet_service_manager::start(config, pools, services).await?;
94101

95102
Ok(())
96103
}
97104
}
105+
106+
/// Verifies that no rollback has occurred (if allowing rollback is disabled).
107+
async fn verify_engine_version(
108+
config: &rivet_config::Config,
109+
pools: &rivet_pools::Pools,
110+
) -> Result<()> {
111+
if config.allow_version_rollback {
112+
return Ok(());
113+
}
114+
115+
pools
116+
.udb()?
117+
.run(|tx| async move {
118+
let current_version = semver::Version::parse(env!("CARGO_PKG_VERSION"))
119+
.context("failed to parse cargo pkg version as semver")?;
120+
121+
if let Some(existing_version) =
122+
tx.read_opt(&keys::EngineVersionKey {}, Serializable).await?
123+
{
124+
if current_version < existing_version {
125+
return Ok(Err(anyhow!("{}", formatdoc!(
126+
"
127+
Rivet Engine has been rolled back to a previous version:
128+
- Last Used Version: {existing_version}
129+
- Current Version: {current_version}
130+
Cannot proceed without potential data corruption.
131+
132+
(If you know what you're doing, this error can be disabled in the Rivet config via `allow_version_rollback: true`)
133+
"
134+
))));
135+
}
136+
}
137+
138+
tx.write(&keys::EngineVersionKey {}, current_version)?;
139+
140+
Ok(Ok(()))
141+
})
142+
.await?
143+
}

engine/packages/engine/src/keys.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use anyhow::Result;
2+
use universaldb::prelude::*;
3+
4+
#[derive(Debug)]
5+
pub struct EngineVersionKey {}
6+
7+
impl EngineVersionKey {
8+
pub fn new() -> Self {
9+
EngineVersionKey {}
10+
}
11+
}
12+
13+
impl FormalKey for EngineVersionKey {
14+
type Value = semver::Version;
15+
16+
fn deserialize(&self, raw: &[u8]) -> Result<Self::Value> {
17+
semver::Version::parse(str::from_utf8(raw)?).map_err(Into::into)
18+
}
19+
20+
fn serialize(&self, value: Self::Value) -> Result<Vec<u8>> {
21+
Ok(value.to_string().into_bytes())
22+
}
23+
}
24+
25+
impl TuplePack for EngineVersionKey {
26+
fn pack<W: std::io::Write>(
27+
&self,
28+
w: &mut W,
29+
tuple_depth: TupleDepth,
30+
) -> std::io::Result<VersionstampOffset> {
31+
let t = (RIVET, VERSION);
32+
t.pack(w, tuple_depth)
33+
}
34+
}
35+
36+
impl<'de> TupleUnpack<'de> for EngineVersionKey {
37+
fn unpack(input: &[u8], tuple_depth: TupleDepth) -> PackResult<(&[u8], Self)> {
38+
let (input, (_, data)) = <(usize, usize)>::unpack(input, tuple_depth)?;
39+
if data != VERSION {
40+
return Err(PackError::Message("expected VERSION data".into()));
41+
}
42+
43+
let v = EngineVersionKey {};
44+
45+
Ok((input, v))
46+
}
47+
}

engine/packages/engine/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use commands::*;
44
use rivet_service_manager::RunConfig;
55

66
pub mod commands;
7+
pub mod keys;
78
pub mod run_config;
89
pub mod util;
910

0 commit comments

Comments
 (0)