You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
iceberg: a dotted database or spoke ID builds an unaddressable namespace — stop emitting one, and migrate the tables already published under one #1129
Arc builds one Iceberg namespace component per database — nsPrefix + "_" + sanitizeNamespaceDB(database) (internal/iceberg/exporter.go, tableIdent) — and sanitizeNamespaceDB maps / to .. So a database name or edge-sync spoke ID carrying a dot produces a single namespace component containing a dot, e.g. arc_rocket.01.
iceberg-go v0.7.0 addresses such a namespace by a JSON-encoded catalog key rather than the plain dotted string, and deliberately refuses the legacy key:
// catalog/sql/sql.go, namespaceStorageKeys// Only marker collisions get a legacy fallback. Dotted components must// never fall back because their legacy key belongs to a different namespace.
Three things then break at once, none of them loudly:
History orphaned. A table published under v0.6.0 is no longer found, so EnsureTable correctly creates a new one. The original table and its whole snapshot history stay on disk, unreferenced, and any external reader pointed at the old path sees a frozen table.
The exporter eats its own metadata. The new directory is __iceberg_namespace_v1__:["arc_rocket.01"].db, which isWarehouseDir (internal/iceberg/source.go) does not recognise — it matches nsPrefix + "_" … .db. So Measurements() walks it as a user database, one nesting level deeper per pass. That is exactly the harm the /-to-. mapping was added to prevent (high(iceberg): edge-sync hub spoke namespaces are exported as garbage tables — source discovery predates #619 #634).
No reader entry point.MetadataLocation() returns a percent-encoded URI while the directory on disk is not encoded, so writeVersionHint publishes nowhere. DuckDB and Spark cannot resolve the table, and the discovery-file failure declines the scheduler's fingerprint cache, so the measurement is re-reconciled on every tick forever.
Current state (mitigated, not fixed)
The iceberg-go v0.7.0 bump ships two refusals so nothing is silently published broken:
iceberg.namespace_prefix containing a dot is refused at config load — it would poison every database on the node.
A database whose namespace component would contain a dot is refused per measurement (checkNamespaceAddressable, called from EnsureTable), logged and skipped, so the rest of the node keeps exporting. This covers the dynamic case a config check cannot: a spoke registering after startup.
So new broken tables are no longer created. What remains unfixed is any table already published under a dotted namespace: it is now refused rather than served, and there is no migration.
How reachable is a dotted database?
Arc's own database names are dot-free by the create-time rule. The one source that permits a dot is an edge-sync spoke ID: validateSpokeID (internal/edgesync/receive.go) rejects /, \, : and .., but a single dot passes — rocket.01, site.a, plant.2 are all valid today. The Iceberg source gets spoke namespaces unconditionally when both features are on (cmd/arc/main.go, icebergSource.SetNamespaceExpander(receivedNamespaces(spokeRegistry))).
What this issue wants
Two stages, and they have to ship together — the first alone would orphan tables the same way the bump does.
Stage 1 — stop building a dotted component. Options, in rough order of preference:
Emit a two-component namespace for a spoke, {nsPrefix + "_" + spoke, db}, instead of folding the separator into a dot. EnsureTable's ns := icetable.Identifier{ident[0]} assumes a one-component namespace and would need widening.
Stage 2 — migrate tables already published under a dotted namespace. Rewrite the catalog row and move the warehouse directory to whatever Stage 1 chooses, preserving the metadata chain and republishing version-hint.text. Needs to be idempotent, safe to interrupt, and to fail closed: a half-migrated table must stay readable at its old location until the new one is complete. Dry-run first, like reconciliation.manifest_only_dry_run.
Worth validating the spoke-ID rule at the same time: if Iceberg export cannot serve a dotted spoke ID, validateSpokeID arguably should reject a dot at registration, which is cheaper than migrating later. That is a compatibility decision for existing spokes, not an obvious yes.
Note
Internal work item. Arc's Iceberg exporter is not taking community contributions, so this is filed for tracking rather than as an invitation to submit a patch.
Refs #634, #750. Found while bumping iceberg-go to v0.7.0.
Summary
Arc builds one Iceberg namespace component per database —
nsPrefix + "_" + sanitizeNamespaceDB(database)(internal/iceberg/exporter.go,tableIdent) — andsanitizeNamespaceDBmaps/to.. So a database name or edge-sync spoke ID carrying a dot produces a single namespace component containing a dot, e.g.arc_rocket.01.iceberg-go v0.7.0 addresses such a namespace by a JSON-encoded catalog key rather than the plain dotted string, and deliberately refuses the legacy key:
Measured on v0.7.0:
Three things then break at once, none of them loudly:
EnsureTablecorrectly creates a new one. The original table and its whole snapshot history stay on disk, unreferenced, and any external reader pointed at the old path sees a frozen table.__iceberg_namespace_v1__:["arc_rocket.01"].db, whichisWarehouseDir(internal/iceberg/source.go) does not recognise — it matchesnsPrefix + "_"….db. SoMeasurements()walks it as a user database, one nesting level deeper per pass. That is exactly the harm the/-to-.mapping was added to prevent (high(iceberg): edge-sync hub spoke namespaces are exported as garbage tables — source discovery predates #619 #634).MetadataLocation()returns a percent-encoded URI while the directory on disk is not encoded, sowriteVersionHintpublishes nowhere. DuckDB and Spark cannot resolve the table, and the discovery-file failure declines the scheduler's fingerprint cache, so the measurement is re-reconciled on every tick forever.Current state (mitigated, not fixed)
The iceberg-go v0.7.0 bump ships two refusals so nothing is silently published broken:
iceberg.namespace_prefixcontaining a dot is refused at config load — it would poison every database on the node.checkNamespaceAddressable, called fromEnsureTable), logged and skipped, so the rest of the node keeps exporting. This covers the dynamic case a config check cannot: a spoke registering after startup.So new broken tables are no longer created. What remains unfixed is any table already published under a dotted namespace: it is now refused rather than served, and there is no migration.
How reachable is a dotted database?
Arc's own database names are dot-free by the create-time rule. The one source that permits a dot is an edge-sync spoke ID:
validateSpokeID(internal/edgesync/receive.go) rejects/,\,:and.., but a single dot passes —rocket.01,site.a,plant.2are all valid today. The Iceberg source gets spoke namespaces unconditionally when both features are on (cmd/arc/main.go,icebergSource.SetNamespaceExpander(receivedNamespaces(spokeRegistry))).What this issue wants
Two stages, and they have to ship together — the first alone would orphan tables the same way the bump does.
Stage 1 — stop building a dotted component. Options, in rough order of preference:
{nsPrefix + "_" + spoke, db}, instead of folding the separator into a dot.EnsureTable'sns := icetable.Identifier{ident[0]}assumes a one-component namespace and would need widening..to_alongside/, accepting thata.banda_bthen collide — which is the collision class More lossy encodings that become paths, keys or authorization decisions #750 is about, so it needs a disambiguator rather than a plain replacement.Stage 2 — migrate tables already published under a dotted namespace. Rewrite the catalog row and move the warehouse directory to whatever Stage 1 chooses, preserving the metadata chain and republishing
version-hint.text. Needs to be idempotent, safe to interrupt, and to fail closed: a half-migrated table must stay readable at its old location until the new one is complete. Dry-run first, likereconciliation.manifest_only_dry_run.Worth validating the spoke-ID rule at the same time: if Iceberg export cannot serve a dotted spoke ID,
validateSpokeIDarguably should reject a dot at registration, which is cheaper than migrating later. That is a compatibility decision for existing spokes, not an obvious yes.Note
Internal work item. Arc's Iceberg exporter is not taking community contributions, so this is filed for tracking rather than as an invitation to submit a patch.
Refs #634, #750. Found while bumping iceberg-go to v0.7.0.