Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,11 @@ public Fileset createMultipleLocationFileset(

try {
store.put(filesetEntity, true /* overwrite */);
} catch (NoSuchEntityException exception) {
// The schema can disappear after the check near the start of this method. The relational
// store detects that race while taking the parent-schema lock; translate its storage-level
// exception into the catalog API's documented missing-schema exception.
throw new NoSuchSchemaException(exception, SCHEMA_DOES_NOT_EXIST_MSG, schemaIdent);
} catch (IOException ioe) {
throw new RuntimeException("Failed to create fileset " + ident, ioe);
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
import static org.apache.gravitino.catalog.kafka.KafkaCatalogPropertiesMetadata.BOOTSTRAP_SERVERS;
import static org.apache.gravitino.catalog.kafka.KafkaTopicPropertiesMetadata.PARTITION_COUNT;
import static org.apache.gravitino.catalog.kafka.KafkaTopicPropertiesMetadata.REPLICATION_FACTOR;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;

import com.google.common.collect.ImmutableMap;
Expand Down Expand Up @@ -72,18 +71,16 @@
import org.apache.gravitino.messaging.Topic;
import org.apache.gravitino.messaging.TopicChange;
import org.apache.gravitino.meta.AuditInfo;
import org.apache.gravitino.meta.BaseMetalake;
import org.apache.gravitino.meta.CatalogEntity;
import org.apache.gravitino.meta.SchemaVersion;
import org.apache.gravitino.storage.IdGenerator;
import org.apache.gravitino.storage.RandomIdGenerator;
import org.apache.gravitino.storage.relational.helper.CatalogIds;
import org.apache.gravitino.storage.relational.service.CatalogMetaService;
import org.apache.gravitino.storage.relational.service.MetalakeMetaService;
import org.apache.kafka.common.config.TopicConfig;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

public class TestKafkaCatalogOperations extends KafkaClusterEmbedded {
Expand Down Expand Up @@ -139,7 +136,7 @@ public PropertiesMetadata modelVersionPropertiesMetadata()
private static KafkaCatalogOperations kafkaCatalogOperations;

@BeforeAll
public static void setUp() throws IllegalAccessException {
public static void setUp() throws IOException, IllegalAccessException {
Config config = Mockito.mock(Config.class);
Mockito.when(config.get(STORE_TRANSACTION_MAX_SKEW_TIME)).thenReturn(1000L);
Mockito.when(config.get(STORE_DELETE_AFTER_TIME)).thenReturn(20 * 60 * 1000L);
Expand Down Expand Up @@ -174,35 +171,23 @@ public static void setUp() throws IllegalAccessException {
Mockito.when(config.get(Configs.CACHE_IMPLEMENTATION)).thenReturn("caffeine");
Mockito.when(config.get(Configs.CACHE_LOCK_SEGMENTS)).thenReturn(16);

// Mock
MetalakeMetaService metalakeMetaService = MetalakeMetaService.getInstance();
MetalakeMetaService spyMetaservice = Mockito.spy(metalakeMetaService);
doReturn(1L).when(spyMetaservice).getMetalakeIdByName(Mockito.anyString());

CatalogMetaService catalogMetaService = CatalogMetaService.getInstance();
CatalogMetaService spyCatalogMetaService = Mockito.spy(catalogMetaService);
doReturn(1L)
.when(spyCatalogMetaService)
.getCatalogIdByMetalakeIdAndName(Mockito.anyLong(), Mockito.anyString());
doReturn(new CatalogIds(1L, 1L))
.when(spyCatalogMetaService)
.getCatalogIdByMetalakeAndCatalogName(Mockito.anyString(), Mockito.anyString());

MockedStatic<MetalakeMetaService> metalakeMetaServiceMockedStatic =
Mockito.mockStatic(MetalakeMetaService.class);
MockedStatic<CatalogMetaService> catalogMetaServiceMockedStatic =
Mockito.mockStatic(CatalogMetaService.class);

metalakeMetaServiceMockedStatic
.when(MetalakeMetaService::getInstance)
.thenReturn(spyMetaservice);
catalogMetaServiceMockedStatic
.when(CatalogMetaService::getInstance)
.thenReturn(spyCatalogMetaService);

store = EntityStoreFactory.createEntityStore(config);
store.initialize(config);
idGenerator = new RandomIdGenerator();

BaseMetalake metalake =
BaseMetalake.builder()
.withId(1L)
.withName(METALAKE_NAME)
.withVersion(SchemaVersion.V_0_1)
.withAuditInfo(
AuditInfo.builder()
.withCreator("testKafkaUser")
.withCreateTime(Instant.now())
.build())
.build();
store.put(metalake, false);

kafkaCatalogEntity =
CatalogEntity.builder()
.withId(1L)
Expand All @@ -217,6 +202,7 @@ public static void setUp() throws IllegalAccessException {
.withCreateTime(Instant.now())
.build())
.build();
store.put(kafkaCatalogEntity, false);

FieldUtils.writeField(GravitinoEnv.getInstance(), "config", config, true);

Expand All @@ -234,11 +220,11 @@ public static void tearDown() throws IOException {
}

@Test
public void testKafkaCatalogConfiguration() {
public void testKafkaCatalogConfiguration() throws IOException {
String catalogName = "test_kafka_catalog_configuration";
CatalogEntity catalogEntity =
CatalogEntity.builder()
.withId(2L)
.withId(idGenerator.nextId())
.withName(catalogName)
.withNamespace(Namespace.of(METALAKE_NAME))
.withType(MESSAGING)
Expand All @@ -250,6 +236,7 @@ public void testKafkaCatalogConfiguration() {
.build())
.withProperties(MOCK_CATALOG_PROPERTIES)
.build();
store.put(catalogEntity, false);
KafkaCatalogOperations ops = new KafkaCatalogOperations(store, idGenerator);
Assertions.assertNull(ops.adminClientConfig);

Expand All @@ -270,11 +257,11 @@ public void testKafkaCatalogConfiguration() {
}

@Test
public void testInitialization() {
public void testInitialization() throws IOException {
String catalogName = "test_kafka_catalog_initialization";
CatalogEntity catalogEntity =
CatalogEntity.builder()
.withId(2L)
.withId(idGenerator.nextId())
.withName(catalogName)
.withNamespace(Namespace.of(METALAKE_NAME))
.withType(MESSAGING)
Expand All @@ -286,6 +273,7 @@ public void testInitialization() {
.build())
.withProperties(MOCK_CATALOG_PROPERTIES)
.build();
store.put(catalogEntity, false);
KafkaCatalogOperations ops = new KafkaCatalogOperations(store, idGenerator);
ops.initialize(
MOCK_CATALOG_PROPERTIES, catalogEntity.toCatalogInfo(), KAFKA_PROPERTIES_METADATA);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.List;
import java.util.Map;
import org.apache.gravitino.Entity;
import org.apache.gravitino.EntityAlreadyExistsException;
import org.apache.gravitino.EntityStore;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.Namespace;
Expand Down Expand Up @@ -116,9 +117,13 @@ public Schema createSchema(NameIdentifier ident, String comment, Map<String, Str
.build())
.build();
try {
store().put(schemaEntity, true /* overwrite */);
// Insert only. Overwriting used to hide a lost race: two servers creating the same schema
// name would both "succeed", and the second one silently replaced the first one's schema.
store().put(schemaEntity, false /* overwrite */);
} catch (IOException ioe) {
throw new RuntimeException("Failed to create schema " + ident, ioe);
} catch (EntityAlreadyExistsException e) {
throw new SchemaAlreadyExistsException(e, "Schema %s already exists", ident);
} catch (NoSuchEntityException e) {
throw new NoSuchCatalogException(e, "Catalog %s does not exist", ident.namespace());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ CatalogPO selectCatalogMetaByName(
method = "selectCatalogMetaByIdForUpdate")
CatalogPO selectCatalogMetaByIdForUpdate(@Param("catalogId") Long catalogId);

/** Selects and share-locks an active catalog by ID for the current transaction. */
@SelectProvider(
type = CatalogMetaSQLProviderFactory.class,
method = "selectCatalogMetaByIdForShare")
CatalogPO selectCatalogMetaByIdForShare(@Param("catalogId") Long catalogId);

@InsertProvider(type = CatalogMetaSQLProviderFactory.class, method = "insertCatalogMeta")
void insertCatalogMeta(@Param("catalogMeta") CatalogPO catalogPO);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,15 @@ public static CatalogMetaBaseSQLProvider getProvider() {

static class CatalogMetaMySQLProvider extends CatalogMetaBaseSQLProvider {}

static class CatalogMetaH2Provider extends CatalogMetaBaseSQLProvider {}
static class CatalogMetaH2Provider extends CatalogMetaBaseSQLProvider {
@Override
public String selectCatalogMetaByIdForShare(Long catalogId) {
// H2 has no shared row-lock syntax, so H2 backends fall back to an exclusive lock. Schema
// creations under one catalog therefore serialize on H2, and a slow creation can make a
// concurrent one hit H2's lock timeout instead of a clean conflict.
return selectCatalogMetaByIdForUpdate(catalogId);
}
}

public static String listCatalogPOsByMetalakeName(@Param("metalakeName") String metalakeName) {
return getProvider().listCatalogPOsByMetalakeName(metalakeName);
Expand Down Expand Up @@ -104,6 +112,11 @@ public static String selectCatalogMetaByIdForUpdate(@Param("catalogId") Long cat
return getProvider().selectCatalogMetaByIdForUpdate(catalogId);
}

/** Returns SQL that selects and share-locks an active catalog by ID. */
public static String selectCatalogMetaByIdForShare(@Param("catalogId") Long catalogId) {
return getProvider().selectCatalogMetaByIdForShare(catalogId);
}

public static String insertCatalogMeta(@Param("catalogMeta") CatalogPO catalogPO) {
return getProvider().insertCatalogMeta(catalogPO);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,18 @@ SchemaPO selectSchemaByFullQualifiedName(
@SelectProvider(type = SchemaMetaSQLProviderFactory.class, method = "selectSchemaMetaById")
SchemaPO selectSchemaMetaById(@Param("schemaId") Long schemaId);

/** Selects and locks an active schema by ID for the current transaction. */
@SelectProvider(
type = SchemaMetaSQLProviderFactory.class,
method = "selectSchemaMetaByIdForUpdate")
SchemaPO selectSchemaMetaByIdForUpdate(@Param("schemaId") Long schemaId);

/** Selects and share-locks an active schema by ID for the current transaction. */
@SelectProvider(
type = SchemaMetaSQLProviderFactory.class,
method = "selectSchemaMetaByIdForShare")
SchemaPO selectSchemaMetaByIdForShare(@Param("schemaId") Long schemaId);

@InsertProvider(type = SchemaMetaSQLProviderFactory.class, method = "insertSchemaMeta")
void insertSchemaMeta(@Param("schemaMeta") SchemaPO schemaPO);

Expand All @@ -111,6 +123,19 @@ Integer updateSchemaMeta(
method = "softDeleteSchemaMetasBySchemaIds")
Integer softDeleteSchemaMetasBySchemaIds(@Param("schemaIds") List<Long> schemaIds);

/**
* Soft-deletes a schema, but only while it still carries the given version.
*
* @param schemaId the ID of the schema to delete
* @param currentVersion the version the caller read before deciding to delete
* @return 1 when the schema was deleted, 0 when it changed or is already gone
*/
@UpdateProvider(
type = SchemaMetaSQLProviderFactory.class,
method = "softDeleteSchemaMetaBySchemaIdAndVersion")
Integer softDeleteSchemaMetaBySchemaIdAndVersion(
@Param("schemaId") Long schemaId, @Param("currentVersion") Long currentVersion);

/**
* Soft-deletes schemas whose identifiers and OCC versions still match.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,15 @@ public static SchemaMetaBaseSQLProvider getProvider() {

static class SchemaMetaMySQLProvider extends SchemaMetaBaseSQLProvider {}

static class SchemaMetaH2Provider extends SchemaMetaBaseSQLProvider {}
static class SchemaMetaH2Provider extends SchemaMetaBaseSQLProvider {
@Override
public String selectSchemaMetaByIdForShare(Long schemaId) {
// H2 has no shared row-lock syntax, so H2 backends fall back to an exclusive lock. Writes of
// tables, views, filesets and the like under one schema therefore serialize on H2, and a slow
// write can make a concurrent one hit H2's lock timeout instead of a clean conflict.
return selectSchemaMetaByIdForUpdate(schemaId);
}
}

public static String listSchemaPOsByFullQualifiedName(
@Param("metalakeName") String metalakeName, @Param("catalogName") String catalogName) {
Expand Down Expand Up @@ -98,6 +106,16 @@ public static String selectSchemaMetaById(@Param("schemaId") Long schemaId) {
return getProvider().selectSchemaMetaById(schemaId);
}

/** Returns SQL that selects and locks an active schema by ID. */
public static String selectSchemaMetaByIdForUpdate(@Param("schemaId") Long schemaId) {
return getProvider().selectSchemaMetaByIdForUpdate(schemaId);
}

/** Returns SQL that selects and share-locks an active schema by ID. */
public static String selectSchemaMetaByIdForShare(@Param("schemaId") Long schemaId) {
return getProvider().selectSchemaMetaByIdForShare(schemaId);
}

public static String insertSchemaMeta(@Param("schemaMeta") SchemaPO schemaPO) {
return getProvider().insertSchemaMeta(schemaPO);
}
Expand Down Expand Up @@ -125,6 +143,11 @@ public static String softDeleteSchemaMetasBySchemaIds(@Param("schemaIds") List<L
return getProvider().softDeleteSchemaMetasBySchemaIds(schemaIds);
}

public static String softDeleteSchemaMetaBySchemaIdAndVersion(
@Param("schemaId") Long schemaId, @Param("currentVersion") Long currentVersion) {
return getProvider().softDeleteSchemaMetaBySchemaIdAndVersion(schemaId, currentVersion);
}

/** Returns SQL that soft-deletes schemas using identifier-and-version pairs. */
public static String softDeleteSchemaMetasWithVersion(
@Param("schemaMetas") List<SchemaPO> schemaPOs) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ public String selectCatalogMetaByIdForUpdate(@Param("catalogId") Long catalogId)
return selectCatalogMetaById(catalogId) + " FOR UPDATE";
}

/** Returns SQL that selects and share-locks an active catalog by ID. */
public String selectCatalogMetaByIdForShare(@Param("catalogId") Long catalogId) {
return selectCatalogMetaById(catalogId) + " LOCK IN SHARE MODE";
}

public String insertCatalogMeta(@Param("catalogMeta") CatalogPO catalogPO) {
return "INSERT INTO "
+ TABLE_NAME
Expand Down
Loading
Loading