yuqi1129 commented on issue #12429:
URL: https://github.com/apache/gravitino/issues/12429#issuecomment-5355764702
Thanks for the detailed write-up. I read the code behind every claim in it,
and they are all correct: the four-step chain in
`GenericCatalogOperations.java:291-322`, the `x-lance-table-location` header in
`docs/lance-rest-service.md:317`, `ManagedTableOperations.java:192-197` doing
nothing to storage, `DropTableEvent.java:29-53` carrying only
user/identifier/`isExists`, and the `ServiceLoader` precedents. Nothing here is
overstated.
I do want to push back on two readings of that code, and then on the shape
of the solution.
## Two places where the quote is right but the reading is off
**1. The `purgeTable` javadoc is not describing a missing feature.**
The issue quotes it as evidence of an architectural gap:
> It only removes the table metadata from the entity store. Physical data
deletion should be handled by the specific catalog implementation if needed.
But that sentence assigns responsibility rather than declining it. Core is
saying the catalog implementation should do this. And one catalog already does:
`FilesetCatalogOperations.java:672-697` calls `fs.delete(location, true)` when
a managed fileset is dropped.
So the real situation is not "Gravitino has no concept of releasing
storage". It is "the generic lakehouse catalog has not implemented the
responsibility core already assigned to it". That difference matters a lot: for
a deployment that just wants the bytes removed, the fix is to do what the
fileset catalog does, and no SPI is involved.
**2. The reason the event mechanism does not work is different from the one
given.**
The issue says events are observational and cannot supply a location. The
conclusion is right, but the reason is not. `EventListenerPlugin` has
`transformPreEvent(SupportsChangingPreEvent)`, whose javadoc says "any changes
to resources in the event will affect the subsequent operations", and the
Iceberg REST server already uses it to rewrite create-table requests
(`IcebergCreateTablePreEvent`).
It does not work for core tables only because `CreateTablePreEvent` does not
implement that interface, and `TableEventDispatcher.createTable` throws away
the return value of `dispatchEvent` and passes the original properties on. Both
are small changes. Worth fixing the wording, because a reviewer who checks will
conclude the alternative was not really examined.
## What the proposal is actually asking for
The issue bundles three different capabilities into one SPI:
1. **Derive** a path from deployment inputs (owning team, domain, region,
tier).
2. **Enforce** that a client cannot pick an arbitrary path.
3. **Allocate** with side effects: reserve the bucket, check quota, register
ownership.
Only (3) needs a plugin. (1) and (2) do not, and Gravitino already has
mechanisms for both.
**For (1):** the fileset catalog has placeholder templating.
`FilesetCatalogOperations.java:1272-1300` builds the substitution map from
every `placeholder-*` property at fileset, schema and catalog level, plus the
built-in catalog/schema/fileset names. A catalog-level template like
`s3://bucket/{{team}}/{{region}}/{{schema}}/{{table}}` with the client passing
`placeholder-team=ads` covers exactly the inputs this issue lists. It is a pure
function, there is nothing to fail, and the template lives in catalog config.
The generic lakehouse catalog simply does not have it.
**For (2):** this is a policy question, and Gravitino has a policy
framework. Policies attach to a catalog, schema or table and inherit down the
hierarchy, which is the same shape as "catalog template, schema override, table
override".
## The part I would think hardest about
Gravitino already has an established pattern for "an external system needs
to act on rules held in metadata". `docs/policies.md` states it directly as a
common use:
> Feeding an external enforcement or scheduling system that reads policies
from Gravitino rather than keeping its own copy of what applies where
The shipped example is `system_iceberg_compaction`: Gravitino stores the
rules, and an external table maintenance service reads them and acts.
The direction of that dependency is external system reads Gravitino. This
proposal reverses it: Gravitino calls an external system, synchronously, inside
a metadata write. That is not just one more extension point; it changes where
Gravitino sits in the architecture, from the authority other systems read to a
client of a system it does not own.
Neither cited precedent supports that direction. `CredentialProvider` is on
a read path and its failures are per request. `LakehouseTableDelegator` is
in-process with no I/O. Neither one puts a remote, side-effecting call inside a
metadata write.
Concretely, what comes with (3):
- **Availability coupling.** If the allocator is down, table creation fails.
The provider is configured per catalog, so that is every table in the catalog.
- **Latency** added to a metadata write, with no timeout, circuit breaker or
fallback specified.
- **Divergence in both directions, and the issue only covers one of them.**
Besides the drop callback that may be lost, there is a create-side leak that is
not mentioned: in `GenericCatalogOperations.java:239-243`,
`calculateTableLocation` runs *before* the table format is validated and before
`tableOps.createTable`. A provider that reserved a bucket can then hit a failed
format check, a `TableAlreadyExistsException`, or a failure inside the format
implementation, leaving the allocator holding a reservation for a table that
does not exist.
- **A reconciler is required anyway.** Since both directions can diverge,
the deployment must reconcile. And a reconciler that lists tables and their
locations through the public API can already find abandoned storage on its own.
That makes the drop callback an optimisation rather than a contract, and I
would rather see it described that way.
- **Locations stop being reproducible.** Two identical create requests can
produce different paths, which affects testing, disaster recovery and migration
between environments.
- **Plugin lifetime.** A `ServiceLoader` plugin living in a catalog
classloader has real hazards here; #11739 is the cautionary tale.
## Alternatives, and what each one covers
| | Covers | Cost |
|---|---|---|
| Bring placeholder templating to the generic lakehouse catalog | derive |
small, mirrors the fileset catalog |
| Location policy / validation, possibly as a built-in policy type | enforce
| medium, no side effects, no availability coupling |
| Implement drop-time deletion in `GenericCatalogOperations` | release, for
self-managed storage | small, mirrors the fileset catalog, and is what the core
javadoc already asks for |
| Add location and properties to `DropTableEvent` | release, for external
allocators | small, and useful to everyone since the event is hard to use for
auditing today |
| Client allocates, server validates (today's flow plus a policy check) |
derive + enforce + allocate | no new architecture |
| The SPI as proposed | all three | everything in the list above |
The first four together cover most of the stated motivation and carry none
of those costs. The "push the problem to the client" option is dismissed in the
issue because nothing stops a client from passing an arbitrary path, but a
policy check is exactly what stops that.
## The question that decides the design
**Why must the allocation be triggered by Gravitino rather than validated by
Gravitino?**
If the allocator can hand out a pool of prefixes in advance, or can accept
lazy registration driven by a reconciler, then client-side allocation plus
server-side validation is enough, and Gravitino stays a metadata service.
The SPI is only necessary if all of the following hold at once: allocation
must happen at the moment of create, the server must be the one to trigger it,
and the result cannot be known in advance. That case is never argued in the
issue, and it is the one thing that would justify the rest.
## Two things to fix either way
**The proposal keeps open the hole it wants to close.** The motivation says
nothing stops a client from passing an arbitrary path, but the wiring says the
provider runs only after the explicit table-level `location` check, so an
explicit location still wins. As specified, a client sending
`x-lance-table-location` bypasses platform policy exactly as it does today. If
server-side enforcement is a goal, whatever makes the decision has to see the
client-supplied location and be able to reject or replace it.
**`location` is a mutable property.**
`GenericTablePropertiesMetadata.java:47` declares it with `immutable = false`,
so `alterTable(setProperty("location", ...))` can repoint a table with the
provider never being told: the old allocation leaks and the new path never went
through policy. Leaving `RenameTable` out of scope is fine, since a rename does
not move data, but changing `location` does.
## Smaller points
- **Module.** The gap is in `ManagedTableOperations` in core, and filesets
have the same problem, which the issue defers. Putting the SPI in
`catalog-lakehouse-generic` makes a parallel fileset version likely later.
- **Lifetime.** `TableLocationProvider extends Closeable`, but who closes
it, and when relative to catalog reload? The cited
`LakehouseTableDelegatorFactory` keeps a static singleton map, while
`initialize(catalogProperties)` implies one instance per catalog.
- **Misconfiguration.** If `table-location-provider` names an implementation
that is not on the classpath, catalog initialisation should fail loudly.
Falling back to the built-in chain would be a silent policy bypass.
- **External tables.** `PROPERTY_EXTERNAL` exists and `location` is
documented as required for them. Is the provider consulted?
## Summary
The problem is real, especially on the drop side. But I would describe that
side as the generic lakehouse catalog not implementing a responsibility core
already assigned, rather than as a missing extension point.
Of the three capabilities bundled here, two already have mechanisms in
Gravitino that the issue does not discuss. The third, side-effecting
synchronous allocation, is the one that genuinely needs a plugin, and it is
also the one that reverses the direction of dependency Gravitino has
established elsewhere.
My suggestion would be to land the small, independently useful pieces first
(placeholders in this catalog, drop-time deletion, richer `DropTableEvent`),
treat location policy as its own discussion, and keep the SPI open until the
case for synchronous, server-triggered allocation is made.
Happy to be wrong on any of this if the allocator really cannot work any
other way. That is the part I would most like to hear more about.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]