Skip to content

[jindo] Support per-request endpoint routing for io-cache - #10350

Open
sundapeng wants to merge 16 commits into
apache:masterfrom
sundapeng:io-cache-routing
Open

sundapeng wants to merge 16 commits into
apache:masterfrom
sundapeng:io-cache-routing

Conversation

@sundapeng

@sundapeng sundapeng commented Oct 3, 2026 •

Copy link
Copy Markdown
Member

Purpose

A REST catalog can vend OSS cache endpoints with the table token, for example an OSS accelerator or a cache cluster in front of the bucket. Today JindoFileIO ignores them unless a JindoCache RPC address is set: it logs "fallback to no-cache" and reads everything from OSS. This PR sends each request to a cache endpoint or to the origin, by operation and file type.

How a request is routed

Every request goes either to a cache target or to the origin, io-cache.origin.endpoint (by default fs.oss.endpoint):

  1. Through a table, only paths under the table's own location may use a target; anything else, such as a file under data-file.external-paths or an object a BLOB descriptor points to, goes to the origin. A FileIO that serves no table reads such row-data URIs from the origin too. Paths that are not oss://, operations not listed in io-cache.policy (read, meta for file status, exists, write), temporary files, files rewritten in place and files named by a sequence id (snapshot-N, schema-N, changelog/changelog-N) go to the origin.
  2. The file type comes from the whole name, in the shape Paimon writes it. A data file is a data or changelog file prefix, including the data-file.prefix and changelog-file.prefix of the table's current and earlier schemas, followed by {uuid}-{count}.{extension}, the same after the postpone writer's -u-{commit user}-s-{write id}-w-, or {uuid}.managed.blob for a paimon-rust managed BLOB pack; its file index adds .index. Metadata is manifest-, manifest-list-, index-manifest- or stat- followed by {uuid}-{count}, a changelog manifest written by paimon-rust is manifest-{uuid}-changelog-{count}, and a manifest may add .avro.sidecar; a bucket index is index-{uuid}-{count}; a global index is <kind>-global-index-{uuid}.index. Any other name, such as an external Format Table file called manifest.parquet, goes to the origin.
  3. The type must be in io-cache.whitelist. With io-cache.routes, the first rule naming the type picks the target; without it, the first target with an endpoint takes every type. A type no rule names goes to the origin.
  4. Listings, deletes, renames, atomic writes and the existence check a writer makes before writing always go to the origin. Errors from a target are returned as they are and never retried on the origin, so each endpoint is expected to be highly available.

A target only ever sees files written once under a unique name, so it can be a read cache that keeps metadata. exists is the exception: Paimon uses it to ask whether a file is still there, for example before replaying a commit on recovery, and a cache that keeps the metadata of deleted objects answers yes after the file is gone. So exists uses a target only when the policy lists it, which suits a target that answers existence as OSS does. The origin sees everything else, so it must answer every request as OSS does: OSS itself, or an endpoint that keeps no metadata cache, such as an OSS accelerator. Format, Object, Iceberg and Lance tables never use a target: other writers may replace their files in place under the same name, whatever the name looks like. A Format Table that should go through an OSS accelerator gets it by making the accelerator the origin.

Endpoint As origin As target
OSS Yes Yes, with any policy
An endpoint that keeps no metadata cache and forwards every request to OSS, such as an OSS accelerator Yes Yes, with meta,read,exists,write
A read cache that keeps metadata, including that of deleted objects No Yes, with meta,read

Usage

A read cache in front of the bucket

io-cache.enabled                          = true
io-cache.endpoint                         = http://10.0.0.1:8080
io-cache.target.default.path-style-access = true
io-cache.policy                           = meta,read
io-cache.whitelist                        = *

Reads and file status of immutable files use the cache; existence checks and everything else use OSS.

An endpoint that also takes writes

io-cache.enabled               = true
io-cache.endpoint              = https://cn-hangzhou-j-internal.oss-data-acc.aliyuncs.com
io-cache.target.default.region = cn-hangzhou
io-cache.policy                = meta,read,exists,write
io-cache.whitelist             = data

Data files are read, written and checked through the OSS accelerator, whose read, write and request paths to OSS add to the direct quotas; everything else uses OSS. exists is listed because the accelerator keeps no metadata cache.

The accelerator as origin, a read cache for data

io-cache.enabled                          = true
io-cache.origin.endpoint                  = https://cn-hangzhou-j-internal.oss-data-acc.aliyuncs.com
io-cache.endpoint                         = http://10.0.0.1:8080
io-cache.target.default.path-style-access = true
io-cache.policy                           = meta,read
io-cache.whitelist                        = data,global-index

Data files and global indexes are read through the cache. Snapshot probes, hint files, manifests, listings and all writes go through the accelerator.

The accelerator as origin, data straight to OSS

io-cache.enabled         = true
io-cache.origin.endpoint = https://cn-hangzhou-j-internal.oss-data-acc.aliyuncs.com
io-cache.endpoint        = https://oss-cn-hangzhou-internal.aliyuncs.com
io-cache.policy          = meta,read,exists,write
io-cache.whitelist       = data

Data files are read and written on the OSS endpoint; metadata and the other request-heavy traffic go through the accelerator, so the two quotas add up.

Two targets by file type

io-cache.enabled                           = true
io-cache.targets                           = accel,cluster
io-cache.target.accel.endpoint             = https://cn-hangzhou-j-internal.oss-data-acc.aliyuncs.com
io-cache.target.accel.region               = cn-hangzhou
io-cache.target.cluster.endpoint           = http://10.0.0.1:8080
io-cache.target.cluster.path-style-access  = true
io-cache.routes                            = meta=accel;data,global-index=cluster
io-cache.policy                            = meta,read

Manifests and statistics are read through the accelerator, data files and global indexes through the cache, and the rest, existence checks included, uses OSS.

Options

Vended with the table token or set in the catalog options.

Option New or existing Meaning
io-cache.enabled Existing Turns the cache on, as before; endpoint routing needs it too.
io-cache.policy Existing; exists is a new value The operations that may use a target: read, meta (file status), exists, write; none turns it off. Endpoint routing matches whole tokens; JindoCache keeps its substring match and exists under meta.
io-cache.whitelist-path Existing, unchanged Path patterns for JindoCache only; endpoint routing does not read it.
io-cache.whitelist New The file types that may use a target: meta, data, bucket-index, global-index, file-index, or * (the default).
io-cache.endpoint New A single target, named default.
io-cache.targets with io-cache.target.<name>.endpoint New Several named targets.
io-cache.target.<name>.region, io-cache.target.<name>.path-style-access New Signing region and path-style addressing of one target, default included.
io-cache.routes New Maps file types to targets; the first rule naming a type wins.
io-cache.origin.endpoint New Where requests that use no target go; by default fs.oss.endpoint, which may point at a cache for clients without routing.

fs.jindocache.namespace.rpc.address (JindoCache mode), dlf.oss-endpoint (turns routing off) and the table options data-file.prefix / changelog-file.prefix (read to recognise data files) are existing options this PR only reads.

In JindoFileIO

  • newInputStream, getFileStatus and exists follow the read, meta and exists rules, and newOutputStream and two-phase multipart writes follow the write rule. Lance and Vortex build their own clients from hadoopOptions(path, op), which cannot route per request, so under routing it gives them the origin's options.
  • Each table applies a view to its FileIO in the AbstractFileStoreTable constructor, which every copy, time travel, branch and fallback branch goes through: the view routes only paths under the table's location and knows the prefixes of the table's schema; the prefixes of the branch's earlier schemas are listed once, through the origin, on the first request whose data file name has none of the current prefixes, and only when routing is on. Format, Object, Iceberg and Lance tables take an origin-only view. RESTTokenFileIO, ResolvingFileIO and CachingFileIO pass the view to the FileIO they wrap when it supports views; a JindoFileIO view shares the file systems of the FileIO it was made from, and closing it closes nothing.
  • The FileIO UriReaderFactory creates for a BLOB descriptor serves no table, so it reads from the origin; a reader over a table's FileIO follows that table's view.
  • copyFile, deleteQuietly and readFileUtf8 keep the FileIO defaults; each read, status lookup and write inside them is routed like any other.
  • RESTTokenFileIO caches one FileIO per token and catalog options. Routing may be configured in the catalog options alone, so catalogs that share a vended token but set different origin or targets keep their own endpoints, while copies of one catalog's RESTTokenFileIO, including deserialized ones, still share one FileIO.
  • One file system per target and bucket. A target that fails to initialize throws the same checked IOException as the origin, so callers such as OrphanFilesClean and ManifestSidecar.read keep their retry and fallback.
  • With fs.jindocache.namespace.rpc.address set, the existing JindoCache mode is used, unchanged. A client-side dlf.oss-endpoint turns routing off.

#10351 (pypaimon) and apache/paimon-rust#1014 implement the same rules.

Tests

  • IoCacheRoutingTest: the endpoint JindoFileIO.hadoopOptions picks for each request with one and two targets, the policy (exists on its own included), whitelist, routes and endpoint options, and the type of each file name, including custom prefixes that collide with reserved names, entropy and postpone directories, sidecars (a ROW sidecar is sized and read through the target of its data file), temporary names, metadata directories, and external names that only look like metadata or indexes (manifest.parquet, stat-2024.parquet, foo.index). It also covers target names, policy parsing and serialization.
  • JindoIoCacheRoutingTest: options of each target, which operations use a target with and without write and exists in the policy, the two-phase write checking the origin, target errors and target initialization errors thrown as checked IOExceptions, JindoCache RPC mode taking precedence, one file system per target and bucket, and close releasing them.
  • TableFileIOViewTest loads tables from a FileSystemCatalog over a FileIO that records its views: copies, branches, external paths, and Format, Object, Iceberg and Lance tables; a FileIO without views, including a forwarding Proxy, stays the same instance in a Format Table, an Object Table and a catalog table.
  • IoCacheTableRoutingTest writes and reads real tables through a FileSystemCatalog whose FileIO routes as JindoFileIO does, with a target that keeps the first copy it served while the origin is replaced: blob-descriptor-field values pointing outside the table (primary-key and append tables), BLOB sources copied into the table and data files under data-file.external-paths come from the origin; files under an older data-file.prefix after ALTER TABLE and a fresh load, branch files under the branch's prefix, and the files a branch created from a tag inherited under main's older prefix use the target.
  • UriReaderFactoryTest, RESTTokenFileIOTest, ResolvingFileIOTest and CachingFileIOTest cover the origin-only descriptor FileIO, the views the wrappers forward and the token FileIO cached per catalog options; JindoIoCacheRoutingTest builds two RESTTokenFileIOs with one token and different catalog-only origin and target endpoints through the real REST cache and JindoFileIO configuration, and checks that each sends reads and writes to its own endpoints and gives Lance its own origin; IoCacheRoutingTest and JindoIoCacheRoutingTest also cover table views (roots, prefixes, earlier schemas loaded once and a failure to load them, shared file systems), postpone names and managed BLOB packs.
  • TestJindoCacheEnable checks that the JindoCache policy matching is unchanged; JindoFileIOTest, TestJindoUserAgent and TestHadoopCompliantFileIODeleteHook still pass.

Verified end to end

All three clients (Java, Python, Rust) created, wrote, read, time-travelled and dropped partitions of tables against local OSS mock endpoints that log every request, with one target, two targets by file type, write routing, and an OSS accelerator as origin with a read cache or OSS as the target. Every request a target saw was one these rules send to it. The pypaimon side (#10351) also ran its cases through a real OSS accelerator, as target and as origin.

API and Format

No storage format change; routing is configured through options. No new types in org.apache.paimon.fs, and the FileIO interface is unchanged. The views live on one internal interface, org.apache.paimon.fs.iocache.IoCacheViewableFileIO (originOnly(), withTableView(TableView)), whose nested TableView and PrefixHistory describe a view; it is internal to io-cache routing, not a public API. JindoFileIO, RESTTokenFileIO, ResolvingFileIO and CachingFileIO implement it; callers go through its static originOnly(FileIO) and withTableView(FileIO, TableView), which return any other FileIO as given, so a FileIO that forwards every method, such as a Proxy in a test, keeps its place.

Documentation

No docs page yet; the options are listed above.

@sundapeng sundapeng changed the title [jindo] Route reads to io-cache targets per file type [WIP] [jindo] Route reads to io-cache targets per file type Oct 3, 2026
@sundapeng sundapeng changed the title [WIP] [jindo] Route reads to io-cache targets per file type [WIP][jindo] Route reads to io-cache targets per file type Oct 3, 2026
@sundapeng
sundapeng force-pushed the io-cache-routing branch 3 times, most recently from afb8b6a to 2ec038d Compare October 3, 2026 15:41
@sundapeng sundapeng changed the title [WIP][jindo] Route reads to io-cache targets per file type [WIP][jindo] Read through cache endpoints by file type Oct 3, 2026
@sundapeng sundapeng changed the title [WIP][jindo] Read through cache endpoints by file type [WIP][jindo] Route reads to io-cache targets by file type Oct 3, 2026
@sundapeng sundapeng closed this Oct 3, 2026
@sundapeng sundapeng reopened this Oct 3, 2026
@sundapeng
sundapeng force-pushed the io-cache-routing branch 2 times, most recently from 06f44a2 to 83cdff5 Compare October 4, 2026 07:58
@JingsongLi
JingsongLi marked this pull request as draft October 4, 2026 08:23
@sundapeng sundapeng changed the title [WIP][jindo] Route reads to io-cache targets by file type [jindo] Route reads to io-cache targets by file type Oct 5, 2026
@sundapeng
sundapeng marked this pull request as ready for review October 5, 2026 06:54
@sundapeng sundapeng closed this Oct 5, 2026
@sundapeng sundapeng reopened this Oct 5, 2026

@JingsongLi JingsongLi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requirement fit: SUPPORTED. Per-file-type cache routing has practical value for REST-vended OSS endpoints. Implementation review found one P2 issue in initialization failure handling.

Validation: 177/188 selected local tests passed; 11 could not initialize Jindo native on macOS ARM. The supported subset passed the normal Maven verification profiles. Same-head Linux CI passed IoCacheRoutingTest (156), JindoIoCacheRoutingTest (10), and JindoFileIOTest (5). A source-free production-class fault-injection probe shows an initialization IOException remains checked on the origin path but becomes UncheckedIOException on the new target path, bypassing ManifestSidecar.read fallback. Invoking the actual OrphanFilesClean.retryReadingFiles helper with a one-shot initialization failure succeeds on the origin path in two attempts; the target path exits in one attempt. Unwrapping the target-path exception in a source-free control restores that fallback. The endpoint public API was also compared against the original production classes (six boundaries each). No live OSS/cache cluster was available.

@JingsongLi JingsongLi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requirement fit: SUPPORTED. Implementation: FINDINGS.

Rechecked revision dd23040, including the new exists routing and origin-only pre-write check. The previously reported P2 is still reproducible: cache-target initialization leaves SocketTimeoutException wrapped in UncheckedIOException, so OrphanFilesClean does not retry and ManifestSidecar.read cannot fall back. The equivalent origin path retries successfully in two attempts and preserves its checked exception. Please address the target computeIfAbsent exception unwrapping in addition to the routing changes.

Validation: normal Maven install and all 176 locally supported routing/cache-policy/user-agent/delete-hook tests passed (159 routing cases). The ten JindoIoCacheRoutingTest cases could not initialize the Jindo native library on macOS ARM; this is the previously identified runtime limitation. Same-head initialization fault-injection probes reconfirmed both public IOException handling and the actual orphan retry/sidecar fallback paths. No live OSS/cache endpoint was available. No additional actionable defect was established in the routing revision.

When the catalog options carry io-cache endpoints instead of a JindoCache
RPC address, JindoFileIO now reads immutable files through a cache target
chosen by file type and keeps every other request on the OSS endpoint.
Before, it logged "fallback to no-cache" and used OSS for everything.

- io-cache.endpoint names one target. io-cache.targets with
  io-cache.target.<name>.endpoint (and optional .region and
  .path-style-access) names several, and io-cache.routes maps file types
  to them. io-cache.whitelist and io-cache.policy keep their meaning, and
  io-cache.origin.endpoint names the OSS endpoint when fs.oss.endpoint
  points at a cache for older clients.
- Only read and meta requests use a target; exists counts as meta. Writes,
  including the existence check before a two-phase write, listings,
  deletes, renames, copies and non-oss paths stay on OSS, and a
  client-side dlf.oss-endpoint turns routing off.
- Files rewritten in place (FileType.isMutable) and metadata named by a
  sequence id never use a target.
- Data files are recognized by name: a data or changelog file prefix,
  including a custom one, followed by {uuid}-{count}.{extension}.
  Manifests, indexes and statistics share the {uuid}-{count} shape but
  have no extension, so a custom prefix such as manifest- cannot mix them
  up; the manifest sidecar is excluded explicitly. Other names keep their
  FileType, and names Paimon does not write stay on OSS.
- Errors from a target are thrown as they are, without retrying on OSS.
  A target that fails to initialize throws the same checked IOException
  as the OSS file system does, so retries and fallbacks still apply.
- The JindoCache RPC mode is unchanged.

The routing cases in src/test/resources/io-cache are shared with pypaimon
and paimon-rust.
@sundapeng
sundapeng marked this pull request as draft October 6, 2026 05:52
deleteQuietly checks a failed delete with exists(), and readFileUtf8 turns
a failed read into FileNotFoundException when exists() says the file is
gone. With cache routing, exists() of a cacheable file goes to a cache,
which can still hold a file deleted on OSS or report a file missing that
OSS has. Both helpers now ask OSS instead.
Some cache endpoints, such as an OSS accelerator, also take writes and
send them to OSS over their own write path, whose bandwidth adds to the
direct one. With write in io-cache.policy, newOutputStream, two-phase
multipart writes and hadoopOptions(path, "write") of cacheable files
(data files, manifests, indexes) now use the file type's target. The
existence check before a two-phase write, atomic writes, renames,
deletes, copies, listings and writes of snapshots, hints and temp files
stay on OSS. A policy with only write routes writes only.
@sundapeng sundapeng changed the title [jindo] Route reads to io-cache targets by file type [jindo] Support per-request endpoint routing for io-cache Oct 6, 2026
…opyFile

The routing FileIO no longer overrides these helpers.

- deleteQuietly only logs a warning when exists() still finds a deleted
  file, so asking a cache changes a log line, not a result.
- readFileUtf8 reports a failed read as not found only when exists() says
  the file is gone. A cacheable file is never probed before it is written,
  so a cache does not report an existing one as missing.
- copyFile is a read plus a write, and each is routed like any other read
  or write now that writes follow io-cache.policy.
Replace the JSON test vectors with assertions grouped by topic in
IoCacheRoutingTest, and drop their license exclusion from the root pom.
@sundapeng
sundapeng marked this pull request as ready for review October 6, 2026 15:47
exists answers whether a file is still there: commit recovery checks the
files it replays, RemoveUnexistingManifests and changelog expiration check
whether a file is gone. A cache that keeps the metadata of deleted objects
answers yes after the file was deleted on OSS, so a recovery could publish
a snapshot that references a deleted file. meta now covers file status
only; exists uses a target only when io-cache.policy lists exists, so the
server vends it only for targets that answer existence as OSS does. The
JindoCache mode keeps exists under meta.
…es them

The broad FileType classifier also matched names such as manifest.parquet,
stat-2024.parquet or foo.index, which a Format Table may hold as ordinary
files and replace in place, so a routed read could return the old file
from a cache. A metadata or index file now uses a target only under the
exact name Paimon writes: manifest-, manifest-list-, index-manifest- and
stat-{uuid}-{n} (a manifest also with .avro.sidecar), index-{uuid}-{n},
and {kind}-global-index-{uuid}.index. Data files and their file indexes
keep the existing name rule. Every other name goes to the origin.
paimon-rust's TableCommit names input-changelog manifests
manifest-{uuid}-changelog-{count}, optionally with .avro.sidecar. They are
written once under a unique name like every other manifest, so the
metadata name rule now accepts the -changelog infix for manifests (not
for manifest lists) and a Java reader of such a table routes them the
same way as paimon-rust does.

@JingsongLi JingsongLi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache-routing use case has end-to-end value. I ran 36 focused Java tests with the standard Maven checks successfully. Another 14 local cases could not initialize Jindo Native on macOS/aarch64, so I do not count them as verified locally. The exact-head Linux CI checks are green. A production-class probe of the native-format handoff reproduces the configuration mismatch below; the probe exercises the actual Jindo, LanceUtils and VortexUtils read/write option translators without requiring native library initialization.

Lance and Vortex take hadoopOptions(path, op) and build their own clients,
which address the endpoint as virtual-hosted HTTPS and cannot reach an
HTTP, IP or path-style io-cache target. With routing, hadoopOptions now
returns the OSS options; the routed options stay package-private for
JindoFileIO and its tests. JindoCache mode is unchanged.
@sundapeng sundapeng closed this Oct 25, 2026
@sundapeng sundapeng reopened this Oct 25, 2026
… io-cache routing

The postpone writer names its files
{prefix}-u-{commit user}-s-{write id}-w-{uuid}-{count}.{extension}, which did
not match {prefix}{uuid}-{count}.{extension}, so their reads and writes always
used the origin. paimon-rust names its managed BLOB packs
{prefix}{uuid}.managed.blob, without the count this client writes. Both are
written once under a unique name and are now data files, with the .index and
.blobref files named after them, so all three clients route the same names
(apache/paimon-rust#1014).
io-cache routing chose the endpoint of a request from the file name alone,
for every table a FileIO served. Review of apache/paimon-rust#1014 found that
a name shows neither who owns a file nor which prefixes a table has used, and
this client has the same gaps:

- Format, Object, Iceberg and Lance tables hold files that other writers may
  replace in place under the same name. Their FileIO is now origin only.
- A row may point at any file. A blob-descriptor-field value, or a BLOB
  written from a descriptor, names a file outside the table that its writer
  may replace, even when it is named like a data file. A FileStoreTable now
  gives io-cache routing its location, and only files under it may use a
  cache target; files under data-file.external-paths are read from origin, as
  without routing. Descriptor readers created from the catalog context serve
  no table, so their FileIO is origin only.
- Data files named with the data-file.prefix or changelog-file.prefix of the
  schema, of a copy's options or of an older schema were not recognized. The
  view passes the prefixes of the table's schema, and the router lists the
  branch's older schemas on OSS, once, the first time a file under the table
  is named like a data file with none of the known prefixes.

FileIO gets two default no-op methods, originOnly() and withTableView().
AbstractFileStoreTable's constructor applies the view, so every copy, time
travel and branch takes the one of its own schema. RESTTokenFileIO,
ResolvingFileIO and CachingFileIO apply the view to the FileIO they wrap; a
JindoFileIO view shares the file systems and the presigner of its base and
closes nothing.

IoCacheTableRoutingTest writes and reads through a FileSystemCatalog over a
FileIO whose target keeps the first copy it served while origin holds a new
one, as the paimon-rust review does: descriptor fields of primary-key and
append tables, a BLOB copied from a descriptor, data files under an external
path, a table read after ALTER of its prefix, and a branch with its own
prefix. Each case but the branch one fails without its part of this change;
the jindo tests cover the router.
…given

The previous commit added originOnly() and withTableView() to FileIO as
default methods returning this. A FileIO decorator that forwards every
method, such as the java.lang.reflect.Proxy wrappers in Spark's
FormatTablePartitionManagementTest, forwards these defaults as well: the
delegate returned itself, so a Format Table got the bare delegate instead of
the decorator, and the decorator's injected failures were lost. Any wrapper
that forwards all FileIO methods would drop itself the same way, and a
default method on the public FileIO interface cannot tell a wrapper from the
FileIO it wraps.

FileIO is unchanged now. The two methods move to IoCacheViewableFileIO,
implemented by the FileIOs that have io-cache routing or wrap one:
JindoFileIO, RESTTokenFileIO, ResolvingFileIO and CachingFileIO, which
forward to a delegate only when it implements the interface. Every caller
goes through IoCacheViews.originOnly() and IoCacheViews.withTableView(),
which return any other FileIO as it is, so Format, Object, Iceberg and Lance
tables and every FileStoreTable keep a decorator they are given.

TableFileIOViewTest gives a forwarding Proxy to a Format Table, an Object
Table and a FileSystemCatalog table and checks each keeps that instance; it
fails on the previous commit.

The same test compared the prefixes of a branch's schemas in listing order,
which is the file system's: LocalFileIO lists the schema directory in
File.list order, which differs between machines. Routing only checks
whether a prefix is known, so the test now ignores the order.

@JingsongLi JingsongLi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requirement fit: SUPPORTED. Per-request OSS endpoint routing has end-to-end value. The previous native-format target handoff is repaired by returning origin options to those consumers. Implementation: FINDINGS.

A clean JDK 8 reactor package passed. Locally, 124 focused tests passed, including actual table read/write/view controls; 21 more could not initialize Jindo Native on macOS/aarch64. I inspected the exact-head Linux JDK 8 CI log: JindoIoCacheRoutingTest (20), JindoFileIOTest (5), and IoCacheRoutingTest (23) all passed, and the full current-head CI matrix is green. A production REST cache/Jindo configuration probe independently reproduces the catalog-only endpoint configuration gap below; it checks actual routing parameters without executing Jindo HTTP.

… it was built from

RESTTokenFileIO keeps the FileIO of each vended token in a static cache, so
every RESTTokenFileIO in the JVM that holds the same token shares it. The
FileIO is built from the catalog options merged with the token, and io-cache
routing may come from the catalog options alone: io-cache.origin.endpoint,
the targets, the policy, the whitelist and the routes. Two catalogs with one
token but different catalog-only routing therefore shared the first one's
JindoFileIO, and the second read and wrote through the first one's origin
and cache.

The cache key is now the token together with an immutable copy of the
catalog options, taken once per RESTTokenFileIO, and the FileIO is built
from that same copy. The catalog's dlf.oss-endpoint and explicit
io-cache.enabled are already merged into the token at refresh. Copies of one
catalog's RESTTokenFileIO, including those deserialized on workers, still
share one FileIO.

RESTTokenFileIOTest checks that the same token under other catalog options
gets its own FileIO. JindoIoCacheRoutingTest builds two RESTTokenFileIOs with
one token and different catalog-only origin and target endpoints through the
real REST cache and JindoFileIO configuration, and checks that each sends
reads and writes to its own endpoints and gives Lance its own origin. Both
fail without the change.
… io-cache tests

A review of the Rust client found two requests that missed their target:
the size check of a ROW sidecar went to origin while its read used the
target, and a REST branch whose schema only the catalog keeps knew no
older prefix of the files it inherited from main.

Neither happens here. DataEvolutionSplitRead sizes the sidecar with
getFileStatus on the table's FileIO, which follows the meta rule, and a
branch reads every older schema from its own schema directory, which
SchemaFilePrefixHistory already lists; without those schemas the read
itself fails before any data request. These tests keep both: a ROW
sidecar is classified as data and sized and read through the target,
and a branch created from a tag reads the files it inherited under
main's older prefix through the target.
The view support added four public types to org.apache.paimon.fs:
IoCacheViewableFileIO, IoCacheViews, TableFileIOView and
FilePrefixHistory. They exist only for io-cache routing, so they now live
in one interface, org.apache.paimon.fs.iocache.IoCacheViewableFileIO,
marked as internal: the static helpers of IoCacheViews become its static
methods, and TableFileIOView and FilePrefixHistory become its nested
TableView and PrefixHistory. org.apache.paimon.fs gains no type and
FileIO is unchanged.

Behavior is the same; every user and test moves to the new names.
@sundapeng sundapeng closed this Oct 29, 2026
@sundapeng sundapeng reopened this Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants