Repository navigation
[#14123] feat(fileset): Support managed and external type conversion - #14124
Conversation
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
3 open issues
What changed in this PR
Adds a new “update fileset type” alter operation across Java, Python, and REST, enabling conversion between MANAGED and EXTERNAL without changing a fileset’s identity, locations, or existing files.
Changes:
- Introduces
FilesetChange.updateType/update_typeand corresponding DTO/request plumbing for REST. - Implements catalog-side application of type changes and adds validation for invalid updateType requests.
- Updates OpenAPI + docs and adds test coverage across server, API, catalog, and Python/Java clients.
| File | Description |
|---|---|
| server/src/test/java/org/apache/gravitino/server/web/rest/TestFilesetOperations.java | Adds REST-layer tests for updateType and invalid request handling. |
| docs/open-api/filesets.yaml | Extends OpenAPI schema with UpdateFilesetTypeRequest. |
| docs/manage-fileset-metadata-using-gravitino.md | Documents the new alter operation and provides examples. |
| docs/filesets.md | Updates lifecycle semantics wording and links to the new conversion operation. |
| common/src/test/java/org/apache/gravitino/dto/requests/TestFilesetUpdatesRequest.java | Adds serialization/validation tests for the new update request. |
| common/src/main/java/org/apache/gravitino/dto/requests/FilesetUpdateRequest.java | Adds UpdateFilesetTypeRequest DTO and validation. |
| clients/client-python/tests/unittests/test_fileset_type_change.py | Adds unit tests for Python update_type and request validation. |
| clients/client-python/gravitino/dto/requests/fileset_update_request.py | Adds Python DTO for updateType. |
| clients/client-python/gravitino/client/fileset_catalog.py | Maps FilesetChange.UpdateFilesetType to DTO requests. |
| clients/client-python/gravitino/api/file/fileset_change.py | Adds Python FilesetChange.update_type and UpdateFilesetType. |
| clients/client-java/src/test/java/org/apache/gravitino/client/TestFilesetCatalog.java | Adds client test for alterFileset with updateType change. |
| clients/client-java/src/main/java/org/apache/gravitino/client/DTOConverters.java | Converts FilesetChange.UpdateFilesetType to REST update request. |
| catalogs/catalog-fileset/src/test/java/org/apache/gravitino/catalog/fileset/TestFilesetCatalogOperations.java | Adds integration-style tests for preservation semantics and drop behavior. |
| catalogs/catalog-fileset/src/main/java/org/apache/gravitino/catalog/fileset/FilesetCatalogOperations.java | Applies UpdateFilesetType when updating persisted entity. |
| api/src/test/java/org/apache/gravitino/file/TestFilesetChange.java | Adds API-level unit tests for FilesetChange.updateType. |
| api/src/main/java/org/apache/gravitino/file/FilesetChange.java | Introduces updateType and UpdateFilesetType change type. |
Review effort: Lite
| **2. Create the fileset.** Give it a name, a type, and a storage location. Choose `MANAGED` to let | ||
| Gravitino manage the data lifecycle, or `EXTERNAL` to retain the data when the fileset is dropped. | ||
| Pointing at an existing path does not automatically make a fileset external. |
There was a problem hiding this comment.
Creation behavior is unchanged. The previous quick-start wording incorrectly implied that an existing path makes a fileset external. I’ll clarify in the PR description that this is a documentation correction.
| /** | ||
| * A fileset change is a change to a fileset. It can be used to rename a fileset, update the comment | ||
| * of a fileset, set a property and value pair for a fileset, or remove a property from a fileset. | ||
| * or type of a fileset, set a property and value pair for a fileset, or remove a property from a | ||
| * fileset. |
There was a problem hiding this comment.
I’ve listed comment updates and type updates separately.
| class FilesetChange(ABC): | ||
| """A fileset change is a change to a fileset. It can be used to rename a fileset, update the comment | ||
| of a fileset, set a property and value pair for a fileset, or remove a property from a fileset. | ||
| or type of a fileset, set a property and value pair for a fileset, or remove a property from a fileset. | ||
| """ |
There was a problem hiding this comment.
I’ve listed the operations separately and kept the wording consistent with the Java API.
jerryshao
left a comment
There was a problem hiding this comment.
Thanks for the PR. The conversion itself looks correct: id, locations and files are preserved, and subsequent drop / schema cascade follow the new type, including multi-location filesets. The main concern is authorization for EXTERNAL -> MANAGED (see inline comment; the auth check lives in FilesetOperations.java:290, outside the diff). A couple of doc clarifications and a Python nit as well.
| newName = ((FilesetChange.RenameFileset) change).getNewName(); | ||
| } else if (change instanceof FilesetChange.UpdateFilesetComment) { | ||
| newComment = ((FilesetChange.UpdateFilesetComment) change).getNewComment(); | ||
| } else if (change instanceof FilesetChange.UpdateFilesetType) { |
There was a problem hiding this comment.
updateType is authorized as a normal alterFileset (FILESET::OWNER or ANY_WRITE_FILESET, see FilesetOperations.java:290), while drop is owner-only. A principal with write but not drop can convert EXTERNAL to MANAGED and thereby make a later owner drop or schema cascade recursively delete every storage location. That is a new way for write to cause data loss. Please require the drop privilege for EXTERNAL to MANAGED, or document and test that write is intentionally enough to accept deletion.
There was a problem hiding this comment.
I kept type conversion under the existing alterFileset authorization, consistent with WRITE_FILESET allowing data writes and metadata changes. I documented this and added an authorization test showing that a writer can convert both ways but still cannot drop the fileset.
| A managed fileset belongs to Gravitino. Creating it creates the directory if needed, and deleting | ||
| it deletes the data. | ||
|
|
||
| An external fileset points at a location that already exists and stays under someone else's control. |
There was a problem hiding this comment.
The quick-start correction is right: an existing path does not make a fileset external. The following sentence still says an external fileset points at a location that already exists. Create mkdirs for both types when the path is missing and adopts an existing directory for both. Please describe type as an explicit lifecycle flag, not as "directory already existed."
There was a problem hiding this comment.
I updated the definition of EXTERNAL to focus on data retention and clarified that either type creates a missing directory when filesystem operations are enabled.
| its identifier, name, storage locations, properties, and existing files are preserved. Setting the | ||
| current type is allowed. | ||
|
|
||
| Change a `MANAGED` fileset to `EXTERNAL` before dropping it to retain its files. Changing an |
There was a problem hiding this comment.
Conversion to MANAGED deletes locations on drop only when filesystem ops are enabled. With disable-filesystem-ops=true, drop still removes only metadata. Worth one sentence next to the new lifecycle text so "accepts deletion" is not read as unconditional.
There was a problem hiding this comment.
I clarified that managed data deletion requires filesystem operations to be enabled. With disable-filesystem-ops=true, drop removes only metadata.
| class UpdateFilesetType: | ||
| """A fileset change to update the type without changing data or locations.""" | ||
|
|
||
| _new_type: Fileset.Type = field(metadata=config(field_name="newType")) |
There was a problem hiding this comment.
nit: UpdateFilesetType works, but please match the other change classes: snake_case field metadata on the change object (keep newType on the request DTO) and an explicit __eq__.
There was a problem hiding this comment.
I switched the change object’s field metadata to new_type and added an explicit __eq__ to match the other change classes. The request DTO still uses newType.
…ntion Validate all location filesystem states before creating directories. Preserve nested new locations and avoid leaving new directories when a later location is rejected. Remove delete-data-on-drop in favor of the separate type-conversion work in apache#14124. Retain the creation guard and explicit adoption option, with regression coverage and updated documentation.


What changes were proposed in this pull request?
Add
FilesetChange.updateType, Pythonupdate_type, and RESTupdateTypesupport for changing existing filesets betweenMANAGEDandEXTERNAL. Conversion preserves the fileset ID, locations, and files. Update the OpenAPI specification and usage documentation.Also correct the quick-start wording that an existing path automatically makes a fileset external. This is a documentation correction; creation behavior is unchanged.
Why are the changes needed?
Users need to change an existing fileset's lifecycle ownership without dropping and recreating it. Converting to external allows its registration to be dropped while retaining the data; converting to managed explicitly accepts deletion on drop.
Related: #12550 and #13506.
Fix: #14123
Does this PR introduce any user-facing change?
Yes. A new fileset alter operation in Java, Python, and REST. Conversion does not move, create, or delete files; subsequent direct and schema-cascade drops use the updated type.
How was this patch tested?
TestFilesetChange,TestFilesetUpdatesRequest,TestFilesetCatalog,TestFilesetOperations, andTestFilesetCatalogOperationswith-PskipITs.python -m unittest tests.unittests.test_fileset_type_change tests.unittests.test_fileset_catalog_api../gradlew :docs:build, Spotless, Black, Pylint, andgit diff --checkpassed.Docker integration tests were not run.