Merge pull request #38 from ModernRelay/fix/mr-670-cleanup-run-branches

Clean up __run__ branch on publish, unblock schema apply (MR-670)
This commit is contained in:
Andrew Altshuler 2026-04-20 13:32:49 +03:00 committed by GitHub
commit a35698e952
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 48 additions and 2 deletions

View file

@ -1497,6 +1497,7 @@ fn json_value_from_array(array: &dyn Array, row: usize) -> Result<serde_json::Va
#[cfg(test)]
mod tests {
use super::*;
use crate::db::is_internal_run_branch;
use crate::db::manifest::ManifestCoordinator;
use async_trait::async_trait;
use omnigraph_compiler::{SchemaMigrationStep, SchemaTypeKind};
@ -1942,6 +1943,47 @@ edge WorksAt: Person -> Company
);
}
#[tokio::test]
async fn test_apply_schema_succeeds_after_load_creates_published_run_branch() {
// Regression for MR-670: schema apply used to fail after any load or
// change because published __run__ branches count as "non-main" in
// the blocking-branch check, and there is no CLI path to clean them
// up (branch_delete rejects internal refs; run abort rejects
// Published runs). Published run branches are intentionally retained
// for post-publish inspection — schema apply now filters them out
// instead of requiring their deletion.
let dir = tempfile::tempdir().unwrap();
let uri = dir.path().to_str().unwrap();
let mut db = Omnigraph::init(uri, TEST_SCHEMA).await.unwrap();
// A load goes through a __run__ branch which remains after publish.
crate::loader::load_jsonl(
&mut db,
r#"{"type": "Person", "data": {"name": "Alice", "age": 30}}"#,
crate::loader::LoadMode::Overwrite,
)
.await
.unwrap();
// Confirm at the coordinator level that a published run branch did
// get created and persists after publish.
let all_branches = db.coordinator.all_branches().await.unwrap();
assert!(
all_branches.iter().any(|b| is_internal_run_branch(b)),
"expected at least one internal run branch after load, got: {:?}",
all_branches
);
// Schema apply should succeed — the filter skips internal system
// branches, including __run__ ones.
let desired = TEST_SCHEMA.replace(
" age: I32?\n}",
" age: I32?\n nickname: String?\n}",
);
let result = db.apply_schema(&desired).await.unwrap();
assert!(result.applied, "schema apply should have applied");
}
#[tokio::test]
async fn test_apply_schema_adds_index_for_existing_property() {
let dir = tempfile::tempdir().unwrap();

View file

@ -32,9 +32,13 @@ pub(super) async fn apply_schema_with_lock(
) -> Result<SchemaApplyResult> {
db.ensure_schema_state_valid().await?;
let branches = db.coordinator.all_branches().await?;
// Skip `main`, the schema-apply lock branch, and any internal `__run__`
// branches. Stale run branches used to block schema apply forever after
// a publish (MR-670); the publish path now cleans them up, but this
// filter is defense-in-depth for legacy repos that predate the fix.
let blocking_branches = branches
.into_iter()
.filter(|branch| branch != "main" && !is_schema_apply_lock_branch(branch))
.filter(|branch| branch != "main" && !is_internal_system_branch(branch))
.collect::<Vec<_>>();
if !blocking_branches.is_empty() {
return Err(OmniError::manifest_conflict(format!(
@ -369,7 +373,7 @@ pub(super) async fn acquire_schema_apply_lock(db: &mut Omnigraph) -> Result<()>
.all_branches()
.await?
.into_iter()
.filter(|branch| branch != "main" && !is_schema_apply_lock_branch(branch))
.filter(|branch| branch != "main" && !is_internal_system_branch(branch))
.collect::<Vec<_>>();
if !blocking_branches.is_empty() {
let _ = release_schema_apply_lock(db).await;