This is an automated email from the ASF dual-hosted git repository.
jshao pushed a commit to branch branch-0.9
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-0.9 by this push:
new 05b13277be [#6813] feat(server): Support update URI for model version
(#7015)
05b13277be is described below
commit 05b13277be5ff0ab9b37857d216fa6d817e281ef
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Apr 21 16:03:35 2025 +0800
[#6813] feat(server): Support update URI for model version (#7015)
### What changes were proposed in this pull request?
Support update URI for model version
- [X] PR1: Add ModelVersionChange API interface, Implement the update
uri logic in model catalog and JDBC backend logic, update related event.
- [ ] PR2: Add REST endpoint to support model version change, add Java
client and Python client for model version comment update.
### Why are the changes needed?
Fix: #6813
### Does this PR introduce _any_ user-facing change?
User can update the uri of a model version.
### How was this patch tested?
local ut test
Co-authored-by: Lord of Abyss
<[email protected]>
---
.../apache/gravitino/model/ModelVersionChange.java | 71 ++++++++++++++++
.../gravitino/model/TestModelVersionChange.java | 44 ++++++++++
.../catalog/model/ModelCatalogOperations.java | 4 +
.../catalog/model/TestModelCatalogOperations.java | 99 ++++++++++++++++++++++
.../catalog/TestModelOperationDispatcher.java | 73 ++++++++++++++++
.../gravitino/connector/TestCatalogOperations.java | 4 +
.../service/TestModelVersionMetaService.java | 83 ++++++++++++++++++
7 files changed, 378 insertions(+)
diff --git
a/api/src/main/java/org/apache/gravitino/model/ModelVersionChange.java
b/api/src/main/java/org/apache/gravitino/model/ModelVersionChange.java
index 9a35c28f85..ced922cffc 100644
--- a/api/src/main/java/org/apache/gravitino/model/ModelVersionChange.java
+++ b/api/src/main/java/org/apache/gravitino/model/ModelVersionChange.java
@@ -61,6 +61,16 @@ public interface ModelVersionChange {
return new ModelVersionChange.RemoveProperty(property);
}
+ /**
+ * Create a ModelVersionChange for updating the uri of a model version.
+ *
+ * @param newUri The new uri to be set for the model version.
+ * @return A new ModelVersionChange instance for updating the uri of a model
version.
+ */
+ static ModelVersionChange updateUri(String newUri) {
+ return new ModelVersionChange.UpdateUri(newUri);
+ }
+
/** A ModelVersionChange to update the model version comment. */
final class UpdateComment implements ModelVersionChange {
@@ -256,4 +266,65 @@ public interface ModelVersionChange {
return "REMOVEPROPERTY " + property;
}
}
+
+ /** A ModelVersionChange to update the uri of a model version. */
+ final class UpdateUri implements ModelVersionChange {
+ private final String newUri;
+
+ /**
+ * Creates a new {@link UpdateUri} instance with the specified new uri.
+ *
+ * @param newUri The new uri to be set for the model version.
+ */
+ public UpdateUri(String newUri) {
+ this.newUri = newUri;
+ }
+
+ /**
+ * Returns the new uri to be set for the model version.
+ *
+ * @return The new uri to be set for the model version.
+ */
+ public String newUri() {
+ return newUri;
+ }
+
+ /**
+ * Compares this UpdateUri instance with another object for equality. The
comparison is based on
+ * the new uri of the model version.
+ *
+ * @param obj The object to compare with this instance.
+ * @return {@code true} if the given object represents the same model
update operation; {@code
+ * false} otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) return true;
+ if (!(obj instanceof UpdateUri)) return false;
+ UpdateUri other = (UpdateUri) obj;
+ return Objects.equals(newUri, other.newUri);
+ }
+
+ /**
+ * Generates a hash code for this UpdateUri instance. The hash code is
based on the new uri of
+ * the model.
+ *
+ * @return A hash code value for this model renaming operation.
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(newUri);
+ }
+
+ /**
+ * Provides a string representation of the UpdateUri instance. This string
format includes the
+ * class name followed by the new uri to be set.
+ *
+ * @return A string summary of the UpdateUri instance.
+ */
+ @Override
+ public String toString() {
+ return "UpdateUri " + newUri;
+ }
+ }
}
diff --git
a/api/src/test/java/org/apache/gravitino/model/TestModelVersionChange.java
b/api/src/test/java/org/apache/gravitino/model/TestModelVersionChange.java
index f978437f27..c4e24110e3 100644
--- a/api/src/test/java/org/apache/gravitino/model/TestModelVersionChange.java
+++ b/api/src/test/java/org/apache/gravitino/model/TestModelVersionChange.java
@@ -155,4 +155,48 @@ public class TestModelVersionChange {
Assertions.assertNotEquals(modelVersionChange1.hashCode(),
modelVersionChange3.hashCode());
Assertions.assertNotEquals(modelVersionChange2.hashCode(),
modelVersionChange3.hashCode());
}
+
+ @Test
+ void testUpdateUriChangeUseStaticMethod() {
+ String newUri = "S3://bucket/key";
+ ModelVersionChange modelVersionChange =
ModelVersionChange.updateUri(newUri);
+
+ Assertions.assertEquals(ModelVersionChange.UpdateUri.class,
modelVersionChange.getClass());
+
+ ModelVersionChange.UpdateUri updateUriChange =
+ (ModelVersionChange.UpdateUri) modelVersionChange;
+ Assertions.assertEquals(newUri, updateUriChange.newUri());
+ Assertions.assertEquals("UpdateUri " + newUri, updateUriChange.toString());
+ }
+
+ @Test
+ void testUpdateUriChangeUseConstructor() {
+ String newUri = "S3://bucket/key";
+ ModelVersionChange modelVersionChange = new
ModelVersionChange.UpdateUri(newUri);
+
+ Assertions.assertEquals(ModelVersionChange.UpdateUri.class,
modelVersionChange.getClass());
+
+ ModelVersionChange.UpdateUri updateUriChange =
+ (ModelVersionChange.UpdateUri) modelVersionChange;
+ Assertions.assertEquals(newUri, updateUriChange.newUri());
+ Assertions.assertEquals("UpdateUri " + newUri, updateUriChange.toString());
+ }
+
+ @Test
+ void testUpdateUriChangeEquals() {
+ String uri1 = "S3://bucket/key1";
+ String uri2 = "S3://bucket/key2";
+
+ ModelVersionChange modelVersionChange1 =
ModelVersionChange.updateUri(uri1);
+ ModelVersionChange modelVersionChange2 =
ModelVersionChange.updateUri(uri1);
+ ModelVersionChange modelVersionChange3 =
ModelVersionChange.updateUri(uri2);
+
+ Assertions.assertEquals(modelVersionChange1, modelVersionChange2);
+ Assertions.assertNotEquals(modelVersionChange1, modelVersionChange3);
+ Assertions.assertNotEquals(modelVersionChange2, modelVersionChange3);
+
+ Assertions.assertEquals(modelVersionChange1.hashCode(),
modelVersionChange2.hashCode());
+ Assertions.assertNotEquals(modelVersionChange1.hashCode(),
modelVersionChange3.hashCode());
+ Assertions.assertNotEquals(modelVersionChange2.hashCode(),
modelVersionChange3.hashCode());
+ }
}
diff --git
a/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
b/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
index d0c9b89cb6..87b0d12d45 100644
---
a/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
+++
b/catalogs/catalog-model/src/main/java/org/apache/gravitino/catalog/model/ModelCatalogOperations.java
@@ -425,6 +425,10 @@ public class ModelCatalogOperations extends
ManagedSchemaOperations
(ModelVersionChange.RemoveProperty) change;
doRemoveProperty(entityProperties, removePropertyChange);
+ } else if (change instanceof ModelVersionChange.UpdateUri) {
+ ModelVersionChange.UpdateUri updateUriChange =
(ModelVersionChange.UpdateUri) change;
+ entityUri = updateUriChange.newUri();
+
} else {
throw new IllegalArgumentException(
"Unsupported model version change: " +
change.getClass().getSimpleName());
diff --git
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
index c84e2fc9cf..77331cf673 100644
---
a/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
+++
b/catalogs/catalog-model/src/test/java/org/apache/gravtitino/catalog/model/TestModelCatalogOperations.java
@@ -1110,6 +1110,105 @@ public class TestModelCatalogOperations {
Assertions.assertEquals(newVersionProperties,
updatedModelVersion.properties());
}
+ @Test
+ void testUpdateModelVersionUri() {
+ String schemaName = randomSchemaName();
+ createSchema(schemaName);
+
+ String modelName = "model1";
+ String modelComment = "model1 comment";
+
+ String versionComment = "version1 comment";
+ String versionUri = "model_version_path";
+ String[] versionAliases = new String[] {"alias1", "alias2"};
+ String newVersionUri = "s3://bucket/path/to/new/version";
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(METALAKE_NAME, CATALOG_NAME, schemaName,
modelName);
+ StringIdentifier stringId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> properties =
StringIdentifier.newPropertiesWithId(stringId, null);
+
+ ops.registerModel(modelIdent, modelComment, properties);
+ StringIdentifier versionId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> versionProperties =
+ StringIdentifier.newPropertiesWithId(
+ versionId, ImmutableMap.of("key1", "value1", "key2", "value2"));
+
+ ops.linkModelVersion(modelIdent, versionUri, versionAliases,
versionComment, versionProperties);
+
+ // validate loaded model
+ Model loadedModel = ops.getModel(modelIdent);
+ Assertions.assertEquals(1, loadedModel.latestVersion());
+
+ // validate loaded version
+ ModelVersion loadedVersion = ops.getModelVersion(modelIdent, 0);
+ Assertions.assertEquals(0, loadedVersion.version());
+ Assertions.assertArrayEquals(versionAliases, loadedVersion.aliases());
+ Assertions.assertEquals(versionComment, loadedVersion.comment());
+ Assertions.assertEquals(versionUri, loadedVersion.uri());
+ Assertions.assertEquals(versionProperties, loadedVersion.properties());
+
+ // validate update version uri
+ ModelVersionChange updateUriChange =
ModelVersionChange.updateUri(newVersionUri);
+ ModelVersion updatedModelVersion = ops.alterModelVersion(modelIdent, 0,
updateUriChange);
+
+ Assertions.assertEquals(0, updatedModelVersion.version());
+ Assertions.assertEquals(newVersionUri, updatedModelVersion.uri());
+ Assertions.assertEquals(versionComment, updatedModelVersion.comment());
+ Assertions.assertArrayEquals(versionAliases,
updatedModelVersion.aliases());
+ Assertions.assertEquals(versionProperties,
updatedModelVersion.properties());
+ }
+
+ @Test
+ void testUpdateModelVersionUriByAlias() {
+ String schemaName = randomSchemaName();
+ createSchema(schemaName);
+
+ String modelName = "model1";
+ String modelComment = "model1 comment";
+
+ String versionComment = "version1 comment";
+ String versionUri = "model_version_path";
+ String[] versionAliases = new String[] {"alias1", "alias2"};
+ String newVersionUri = "s3://bucket/path/to/new/version";
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(METALAKE_NAME, CATALOG_NAME, schemaName,
modelName);
+ StringIdentifier stringId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> properties =
StringIdentifier.newPropertiesWithId(stringId, null);
+
+ ops.registerModel(modelIdent, modelComment, properties);
+ StringIdentifier versionId = StringIdentifier.fromId(idGenerator.nextId());
+ Map<String, String> versionProperties =
+ StringIdentifier.newPropertiesWithId(
+ versionId, ImmutableMap.of("key1", "value1", "key2", "value2"));
+
+ ops.linkModelVersion(modelIdent, versionUri, versionAliases,
versionComment, versionProperties);
+
+ // validate loaded model
+ Model loadedModel = ops.getModel(modelIdent);
+ Assertions.assertEquals(1, loadedModel.latestVersion());
+
+ // validate loaded version
+ ModelVersion loadedVersion = ops.getModelVersion(modelIdent,
versionAliases[0]);
+ Assertions.assertEquals(0, loadedVersion.version());
+ Assertions.assertArrayEquals(versionAliases, loadedVersion.aliases());
+ Assertions.assertEquals(versionComment, loadedVersion.comment());
+ Assertions.assertEquals(versionUri, loadedVersion.uri());
+ Assertions.assertEquals(versionProperties, loadedVersion.properties());
+
+ // validate update version uri
+ ModelVersionChange updateUriChange =
ModelVersionChange.updateUri(newVersionUri);
+ ModelVersion updatedModelVersion =
+ ops.alterModelVersion(modelIdent, versionAliases[0], updateUriChange);
+
+ Assertions.assertEquals(0, updatedModelVersion.version());
+ Assertions.assertEquals(newVersionUri, updatedModelVersion.uri());
+ Assertions.assertEquals(versionComment, updatedModelVersion.comment());
+ Assertions.assertArrayEquals(versionAliases,
updatedModelVersion.aliases());
+ Assertions.assertEquals(versionProperties,
updatedModelVersion.properties());
+ }
+
private String randomSchemaName() {
return "schema_" + UUID.randomUUID().toString().replace("-", "");
}
diff --git
a/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
b/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
index 5e27a77ef1..f059b857c1 100644
---
a/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
+++
b/core/src/test/java/org/apache/gravitino/catalog/TestModelOperationDispatcher.java
@@ -554,6 +554,79 @@ public class TestModelOperationDispatcher extends
TestOperationDispatcher {
Assertions.assertEquals(newProps, alteredModelVersion.properties());
}
+ @Test
+ void testUpdateModelVersionUri() {
+ String schemaName = randomSchemaName();
+ String schemaComment = "schema which tests update";
+
+ String modelName = randomModelName();
+ String modelComment = "model which tests update";
+ Map<String, String> props = ImmutableMap.of("k1", "v1", "k2", "v2");
+ String newUri = "s3://new-bucket/new-path/new-model.json";
+
+ String versionUri = "s3://test-bucket/test-path/model.json";
+ String[] versionAliases = {"alias1", "alias2"};
+ String versionComment = "version which tests update";
+
+ NameIdentifier schemaIdent = NameIdentifier.of(metalake, catalog,
schemaName);
+ schemaOperationDispatcher.createSchema(schemaIdent, schemaComment, props);
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(metalake, catalog, schemaName, modelName);
+ modelOperationDispatcher.registerModel(modelIdent, modelComment, props);
+
+ modelOperationDispatcher.linkModelVersion(
+ modelIdent, versionUri, versionAliases, versionComment, props);
+
+ ModelVersionChange change = ModelVersionChange.updateUri(newUri);
+ ModelVersion modelVersion =
modelOperationDispatcher.getModelVersion(modelIdent, 0);
+ ModelVersion alteredModelVersion =
+ modelOperationDispatcher.alterModelVersion(modelIdent, 0, change);
+
+ Assertions.assertEquals(newUri, alteredModelVersion.uri());
+ Assertions.assertEquals(modelVersion.version(),
alteredModelVersion.version());
+ Assertions.assertEquals(modelVersion.aliases(),
alteredModelVersion.aliases());
+ Assertions.assertEquals(modelVersion.comment(),
alteredModelVersion.comment());
+ Assertions.assertEquals(modelVersion.properties(),
alteredModelVersion.properties());
+ }
+
+ @Test
+ void testUpdateModelVersionUriByAlias() {
+ String schemaName = randomSchemaName();
+ String schemaComment = "schema which tests update";
+
+ String modelName = randomModelName();
+ String modelComment = "model which tests update";
+ Map<String, String> props = ImmutableMap.of("k1", "v1", "k2", "v2");
+ String newUri = "s3://new-bucket/new-path/new-model.json";
+
+ String versionUri = "s3://test-bucket/test-path/model.json";
+ String[] versionAliases = {"alias1", "alias2"};
+ String versionComment = "version which tests update";
+
+ NameIdentifier schemaIdent = NameIdentifier.of(metalake, catalog,
schemaName);
+ schemaOperationDispatcher.createSchema(schemaIdent, schemaComment, props);
+
+ NameIdentifier modelIdent =
+ NameIdentifierUtil.ofModel(metalake, catalog, schemaName, modelName);
+ modelOperationDispatcher.registerModel(modelIdent, modelComment, props);
+
+ modelOperationDispatcher.linkModelVersion(
+ modelIdent, versionUri, versionAliases, versionComment, props);
+
+ ModelVersionChange change = ModelVersionChange.updateUri(newUri);
+ ModelVersion modelVersion =
+ modelOperationDispatcher.getModelVersion(modelIdent,
versionAliases[0]);
+ ModelVersion alteredModelVersion =
+ modelOperationDispatcher.alterModelVersion(modelIdent,
versionAliases[0], change);
+
+ Assertions.assertEquals(newUri, alteredModelVersion.uri());
+ Assertions.assertEquals(modelVersion.version(),
alteredModelVersion.version());
+ Assertions.assertEquals(modelVersion.aliases(),
alteredModelVersion.aliases());
+ Assertions.assertEquals(modelVersion.comment(),
alteredModelVersion.comment());
+ Assertions.assertEquals(modelVersion.properties(),
alteredModelVersion.properties());
+ }
+
private String randomSchemaName() {
return "schema_" + UUID.randomUUID().toString().replace("-", "");
}
diff --git
a/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
b/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
index 94dcd420f0..a0105e0878 100644
---
a/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
+++
b/core/src/test/java/org/apache/gravitino/connector/TestCatalogOperations.java
@@ -1055,6 +1055,10 @@ public class TestCatalogOperations
ModelVersionChange.SetProperty setProperty =
(ModelVersionChange.SetProperty) change;
newProps.put(setProperty.property(), setProperty.value());
+ } else if (change instanceof ModelVersionChange.UpdateUri) {
+ ModelVersionChange.UpdateUri updateUriChange =
(ModelVersionChange.UpdateUri) change;
+ newUri = updateUriChange.newUri();
+
} else {
throw new IllegalArgumentException("Unsupported model change: " +
change);
}
diff --git
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelVersionMetaService.java
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelVersionMetaService.java
index 4284537d76..38bfc1e94e 100644
---
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelVersionMetaService.java
+++
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestModelVersionMetaService.java
@@ -632,6 +632,89 @@ public class TestModelVersionMetaService extends
TestJDBCBackend {
updatePropertiesUpdater));
}
+ @Test
+ void testUpdateModelVersionUri() throws IOException {
+ createParentEntities(METALAKE_NAME, CATALOG_NAME, SCHEMA_NAME, auditInfo);
+
+ Map<String, String> properties = ImmutableMap.of("k1", "v1", "k2", "v2");
+ String modelName = randomModelName();
+ String modelComment = "model1 comment";
+ String modelVersionUri = "S3://test/path/to/model/version";
+ List<String> modelVersionAliases = ImmutableList.of("alias1", "alias2");
+ String modelVersionComment = "test comment";
+ String updatedUri = "S3://test/path/to/new/model/version";
+ int version = 0;
+
+ ModelEntity modelEntity =
+ createModelEntity(
+ RandomIdGenerator.INSTANCE.nextId(),
+ MODEL_NS,
+ modelName,
+ modelComment,
+ 0,
+ properties,
+ auditInfo);
+
+ ModelVersionEntity modelVersionEntity =
+ createModelVersionEntity(
+ modelEntity.nameIdentifier(),
+ version,
+ modelVersionUri,
+ modelVersionAliases,
+ modelVersionComment,
+ properties,
+ auditInfo);
+
+ ModelVersionEntity updatedModelVersionEntity =
+ createModelVersionEntity(
+ modelVersionEntity.modelIdentifier(),
+ modelVersionEntity.version(),
+ updatedUri,
+ modelVersionEntity.aliases(),
+ modelVersionEntity.comment(),
+ modelVersionEntity.properties(),
+ modelVersionEntity.auditInfo());
+
+ Assertions.assertDoesNotThrow(
+ () -> ModelMetaService.getInstance().insertModel(modelEntity, false));
+
+ Assertions.assertDoesNotThrow(
+ () ->
ModelVersionMetaService.getInstance().insertModelVersion(modelVersionEntity));
+
+ Function<ModelVersionEntity, ModelVersionEntity> updatePropertiesUpdater =
+ oldModelVersionEntity -> updatedModelVersionEntity;
+
+ ModelVersionEntity alteredModelVersionEntity =
+ ModelVersionMetaService.getInstance()
+ .updateModelVersion(modelVersionEntity.nameIdentifier(),
updatePropertiesUpdater);
+
+ Assertions.assertEquals(updatedModelVersionEntity,
alteredModelVersionEntity);
+
+ // Test update a non-exist model
+ Assertions.assertThrows(
+ NoSuchEntityException.class,
+ () ->
+ ModelVersionMetaService.getInstance()
+ .updateModelVersion(
+ NameIdentifierUtil.ofModelVersion(
+ METALAKE_NAME,
+ CATALOG_NAME,
+ SCHEMA_NAME,
+ "non_exist_model",
+ "non_exist_version"),
+ updatePropertiesUpdater));
+
+ // Test update a non-exist model version
+ Assertions.assertThrows(
+ NoSuchEntityException.class,
+ () ->
+ ModelVersionMetaService.getInstance()
+ .updateModelVersion(
+ NameIdentifierUtil.ofModelVersion(
+ METALAKE_NAME, CATALOG_NAME, SCHEMA_NAME, modelName,
"non_exist_version"),
+ updatePropertiesUpdater));
+ }
+
private NameIdentifier getModelVersionIdent(NameIdentifier modelIdent, int
version) {
List<String> parts = Lists.newArrayList(modelIdent.namespace().levels());
parts.add(modelIdent.name());