From 8d9c351769e153e6e6c8d032d3925d21335be563 Mon Sep 17 00:00:00 2001 From: Matt Katz Date: Wed, 16 Sep 2026 14:23:13 -0400 Subject: [PATCH 1/3] Skip edition-enabled encodings that have no registered plugin when writing Signed-off-by: Matt Katz --- vortex-file/src/writer.rs | 40 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/vortex-file/src/writer.rs b/vortex-file/src/writer.rs index 6c0d01dfa05..b2446fac0bc 100644 --- a/vortex-file/src/writer.rs +++ b/vortex-file/src/writer.rs @@ -394,11 +394,17 @@ fn new_array_context(session: &VortexSession, enforce_editions: bool) -> ArrayCo // // The seeded IDs are also what the writer may emit: callers read them back with // `ArrayContext::to_ids` to restrict compression to the same set. + let arrays = session.arrays(); let serialized_ids: Vec = if enforce_editions { - session.enabled_component_ids(ComponentKind::Array) - } else { + // An edition may enable an encoding whose plugin is not registered on this session. + // Nothing could serialize it, so it is neither seeded nor offered to the compressor. session - .arrays() + .enabled_component_ids(ComponentKind::Array) + .into_iter() + .filter(|id| arrays.registry().get(id).is_some()) + .collect() + } else { + arrays .registry() .read(|registry| registry.keys().copied().collect()) }; @@ -764,6 +770,7 @@ mod tests { use vortex_edition::EditionMember; use vortex_edition::EditionSession; use vortex_edition::EditionSessionExt; + use vortex_session::registry::CachedId; use super::*; @@ -788,6 +795,33 @@ mod tests { Ok(()) } + /// An edition may enable an encoding whose plugin is not registered on the session. Nothing + /// could serialize it, so it is neither seeded into the table nor offered to the compressor. + #[test] + fn array_context_skips_enabled_but_unregistered_encodings() -> VortexResult<()> { + const EDITION: EditionId = EditionId::new("test", 2026, 9, 0); + static DECLARATION: EditionDeclaration = EditionDeclaration { + edition: Edition { + id: EDITION, + min_library_version: None, + }, + added: &[ + EditionMember::array(&"vortex.primitive"), + EditionMember::array(&"vortex.alp"), + ], + }; + + let session = array_session().with::(); + session.register_edition(&DECLARATION)?; + session.enable_edition(EDITION)?; + + let ctx = new_array_context(&session, true); + assert_eq!(ctx.to_ids(), [Primitive.id()]); + static ALP: CachedId = CachedId::new("vortex.alp"); + assert!(ctx.intern(&ALP).is_none()); + Ok(()) + } + #[test] fn disabling_editions_allows_all_registered_array_ids() { let session = array_session(); From 038f59c68b2c2581f0922fe7dd16209d06039a29 Mon Sep 17 00:00:00 2001 From: Matt Katz Date: Wed, 16 Sep 2026 14:34:01 -0400 Subject: [PATCH 2/3] Snapshot the registered array IDs once when building the writer context Signed-off-by: Matt Katz --- vortex-file/src/writer.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/vortex-file/src/writer.rs b/vortex-file/src/writer.rs index b2446fac0bc..060ba18ad1b 100644 --- a/vortex-file/src/writer.rs +++ b/vortex-file/src/writer.rs @@ -394,19 +394,20 @@ fn new_array_context(session: &VortexSession, enforce_editions: bool) -> ArrayCo // // The seeded IDs are also what the writer may emit: callers read them back with // `ArrayContext::to_ids` to restrict compression to the same set. - let arrays = session.arrays(); + let registered: HashSet = session + .arrays() + .registry() + .read(|registry| registry.keys().copied().collect()); let serialized_ids: Vec = if enforce_editions { // An edition may enable an encoding whose plugin is not registered on this session. // Nothing could serialize it, so it is neither seeded nor offered to the compressor. session .enabled_component_ids(ComponentKind::Array) .into_iter() - .filter(|id| arrays.registry().get(id).is_some()) + .filter(|id| registered.contains(id)) .collect() } else { - arrays - .registry() - .read(|registry| registry.keys().copied().collect()) + registered.into_iter().collect() }; let array_ctx = ArrayContext::new(serialized_ids.iter().copied().sorted().collect()); if enforce_editions { From 6fce0df6f518236d102b2ee09312824c4546fe34 Mon Sep 17 00:00:00 2001 From: Matt Katz Date: Wed, 16 Sep 2026 14:59:48 -0400 Subject: [PATCH 3/3] Explain why the writer intersects edition IDs with the registry Signed-off-by: Matt Katz --- vortex-file/src/writer.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/vortex-file/src/writer.rs b/vortex-file/src/writer.rs index 060ba18ad1b..874d08be306 100644 --- a/vortex-file/src/writer.rs +++ b/vortex-file/src/writer.rs @@ -391,16 +391,18 @@ fn new_array_context(session: &VortexSession, enforce_editions: bool) -> ArrayCo // serialised array order is deterministic. The serialisation of arrays are done // parallel and with an empty context they can register their encodings to the context // in different order, changing the written bytes from run to run. - // - // The seeded IDs are also what the writer may emit: callers read them back with - // `ArrayContext::to_ids` to restrict compression to the same set. + + // The registry keys are exactly the serialized IDs that can be produced by the plugins + // registered in the session. Intersected with the serialized IDs the enabled editions permit, + // this is the set of IDs that can be written. Only seed the array context with these IDs, and + // only allow compressor schemes that produce arrays with serialized IDs within this set. let registered: HashSet = session .arrays() .registry() .read(|registry| registry.keys().copied().collect()); let serialized_ids: Vec = if enforce_editions { - // An edition may enable an encoding whose plugin is not registered on this session. - // Nothing could serialize it, so it is neither seeded nor offered to the compressor. + // Must filter by the set of serialized IDs supported by registered plugins. Otherwise, + // we could enable an ID that will never be written. session .enabled_component_ids(ComponentKind::Array) .into_iter()