Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change default key column size and make it configurable for MySQL and Oracle #2245

Merged
merged 7 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
6 changes: 6 additions & 0 deletions core/src/main/java/com/scalar/db/common/error/CoreError.java
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,12 @@ public enum CoreError implements ScalarDbError {
"ScalarDB Transparent Data Encryption is not enabled. To use ScalarDB Transparent Data Encryption, you must enable it. Note that this feature is supported only in the ScalarDB Enterprise edition",
"",
""),
INVALID_VARIABLE_KEY_COLUMN_SIZE(
Category.USER_ERROR,
"0144",
"The variable key column size must be greater than or equal to 64",
"",
""),

//
// Errors for the concurrency error category
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ public ConditionalMutator(
Mutation mutation,
TableMetadata tableMetadata,
Connection connection,
QueryBuilder queryBuilder)
throws SQLException {
RdbEngineStrategy rdbEngine,
QueryBuilder queryBuilder) {
assert mutation.getCondition().isPresent();
this.mutation = mutation;
this.tableMetadata = tableMetadata;
this.connection = connection;
this.rdbEngine = rdbEngine;
this.queryBuilder = queryBuilder;
rdbEngine = RdbEngineFactory.create(connection);
}

// For the SpotBugs warning CT_CONSTRUCTOR_THROW
Expand Down
48 changes: 48 additions & 0 deletions core/src/main/java/com/scalar/db/storage/jdbc/JdbcConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ public class JdbcConfig {
public static final String ADMIN_CONNECTION_POOL_MAX_TOTAL =
PREFIX + "admin.connection_pool.max_total";

public static final String MYSQL_VARIABLE_KEY_COLUMN_SIZE =
PREFIX + "mysql.variable_key_column_size";
public static final String ORACLE_VARIABLE_KEY_COLUMN_SIZE =
PREFIX + "oracle.variable_key_column_size";
Comment on lines +50 to +53
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I introduced two separate settings (only) for MySQL and Oracle for the following reasons.

  • PostgreSQL also has a limit of 10485760 for each key column (but not for total). So, users might be confused if the common setting jdbc.variable_key_column_size does not affect it.
  • We can also make the limit of PostgreSQL configurable, but the common settings would decrease the current limit since 10485760 is too large for MySQL and Oracle.
  • I guess there is no motivation to decrease 10485760 for PostgreSQL.

Copy link
Contributor

@Torch3333 Torch3333 Sep 25, 2024

Choose a reason for hiding this comment

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

What is the reasoning for adding variable in the configuration name?
In essence, it is a configuration, implying that the value is configurable (== variable). Following the same logic, we could have added variable in all the other configuration names.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Torch3333 Thank you for the question. It's a good point. It doesn't mean it's configurable, but the size of the variable (data-type) key column. I would like to distinguish it from other fixed-size data types like int and double since this option only affects data types (e.g., varchar and varbinary) with variable length. Still weird?

Copy link
Contributor

@Torch3333 Torch3333 Sep 26, 2024

Choose a reason for hiding this comment

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

Oh, ok. I completely missed this interpretation. I agree with this reasoning. 👍
Thank you for the explanation.


public static final int DEFAULT_CONNECTION_POOL_MIN_IDLE = 20;
public static final int DEFAULT_CONNECTION_POOL_MAX_IDLE = 50;
public static final int DEFAULT_CONNECTION_POOL_MAX_TOTAL = 200;
Expand All @@ -61,6 +66,22 @@ public class JdbcConfig {
public static final int DEFAULT_ADMIN_CONNECTION_POOL_MAX_IDLE = 10;
public static final int DEFAULT_ADMIN_CONNECTION_POOL_MAX_TOTAL = 25;

// MySQL and Oracle have limitations regarding the total size of key columns. Thus, we should set
// a small but enough key column size so that users can create multiple key columns without
// exceeding the limit and changing the default. Since we found the old default size of 64 bytes
// was small for some applications, we changed it based on the following specifications. See the
// official documents for details.
// 1) In MySQL, the maximum total size of key columns is 3072 bytes in the default, and thus,
// depending on the charset, it can be up to 768 characters long. It can further be reduced if the
// different settings are used.
// 2) In Oracle, the maximum total size of key columns is approximately 75% of the database block
// size minus some overhead. The default block size is 8KB, and it is typically 4kB or 8kB. Thus,
// the maximum size can be similar to the MySQL.
// See the official documents for details.
// https://dev.mysql.com/doc/refman/8.0/en/innodb-limits.html
// https://docs.oracle.com/en/database/oracle/oracle-database/23/refrn/logical-database-limits.html
Comment on lines +69 to +82
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

public static final int DEFAULT_VARIABLE_KEY_COLUMN_SIZE = 128;

private final String jdbcUrl;
@Nullable private final String username;
@Nullable private final String password;
Expand All @@ -82,6 +103,9 @@ public class JdbcConfig {
private final int adminConnectionPoolMaxIdle;
private final int adminConnectionPoolMaxTotal;

private final int mysqlVariableKeyColumnSize;
private final int oracleVariableKeyColumnSize;

public JdbcConfig(DatabaseConfig databaseConfig) {
String storage = databaseConfig.getStorage();
String transactionManager = databaseConfig.getTransactionManager();
Expand Down Expand Up @@ -167,6 +191,22 @@ public JdbcConfig(DatabaseConfig databaseConfig) {
ADMIN_CONNECTION_POOL_MAX_TOTAL,
DEFAULT_ADMIN_CONNECTION_POOL_MAX_TOTAL);

mysqlVariableKeyColumnSize =
getInt(
databaseConfig.getProperties(),
MYSQL_VARIABLE_KEY_COLUMN_SIZE,
DEFAULT_VARIABLE_KEY_COLUMN_SIZE);

oracleVariableKeyColumnSize =
getInt(
databaseConfig.getProperties(),
ORACLE_VARIABLE_KEY_COLUMN_SIZE,
DEFAULT_VARIABLE_KEY_COLUMN_SIZE);

if (mysqlVariableKeyColumnSize < 64 || oracleVariableKeyColumnSize < 64) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's probably better to make 64 a static variable like `MINIMUM_VARIABLE_KEY_COLUMN_SIZE.
We should probably note why 64.

Copy link
Contributor Author

@jnmt jnmt Sep 26, 2024

Choose a reason for hiding this comment

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

@feeblefakie Definitely, thank you! I updated it with the reason based on Mitsu's comment in 1072e98. PTAL, just in case.

throw new IllegalArgumentException(CoreError.INVALID_VARIABLE_KEY_COLUMN_SIZE.buildMessage());
}

if (databaseConfig.getProperties().containsKey(TABLE_METADATA_SCHEMA)) {
logger.warn(
"The configuration property \""
Expand Down Expand Up @@ -250,4 +290,12 @@ public int getAdminConnectionPoolMaxIdle() {
public int getAdminConnectionPoolMaxTotal() {
return adminConnectionPoolMaxTotal;
}

public int getMysqlVariableKeyColumnSize() {
return mysqlVariableKeyColumnSize;
}

public int getOracleVariableKeyColumnSize() {
return oracleVariableKeyColumnSize;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import com.scalar.db.exception.storage.ExecutionException;
import com.scalar.db.exception.storage.NoMutationException;
import com.scalar.db.exception.storage.RetriableExecutionException;
import com.scalar.db.storage.jdbc.query.QueryBuilder;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
Expand Down Expand Up @@ -60,8 +59,7 @@ public JdbcDatabase(DatabaseConfig databaseConfig) {
databaseConfig.getMetadataCacheExpirationTimeSecs());

OperationChecker operationChecker = new OperationChecker(databaseConfig, tableMetadataManager);
QueryBuilder queryBuilder = new QueryBuilder(rdbEngine);
jdbcService = new JdbcService(tableMetadataManager, operationChecker, queryBuilder);
jdbcService = new JdbcService(tableMetadataManager, operationChecker, rdbEngine);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Push down RdbEngineStrategy since we don't have to create it again at the deeper level (i.e., ConditionMutator).

}

@VisibleForTesting
Expand Down
18 changes: 16 additions & 2 deletions core/src/main/java/com/scalar/db/storage/jdbc/JdbcService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.annotations.VisibleForTesting;
import com.scalar.db.api.Delete;
import com.scalar.db.api.Get;
import com.scalar.db.api.Mutation;
Expand Down Expand Up @@ -41,15 +42,26 @@ public class JdbcService {

private final TableMetadataManager tableMetadataManager;
private final OperationChecker operationChecker;
private final RdbEngineStrategy rdbEngine;
private final QueryBuilder queryBuilder;

@SuppressFBWarnings("EI_EXPOSE_REP2")
public JdbcService(
TableMetadataManager tableMetadataManager,
OperationChecker operationChecker,
RdbEngineStrategy rdbEngine) {
this(tableMetadataManager, operationChecker, rdbEngine, new QueryBuilder(rdbEngine));
}

@VisibleForTesting
JdbcService(
TableMetadataManager tableMetadataManager,
OperationChecker operationChecker,
RdbEngineStrategy rdbEngine,
QueryBuilder queryBuilder) {
this.tableMetadataManager = Objects.requireNonNull(tableMetadataManager);
this.operationChecker = Objects.requireNonNull(operationChecker);
this.rdbEngine = Objects.requireNonNull(rdbEngine);
this.queryBuilder = Objects.requireNonNull(queryBuilder);
}

Expand Down Expand Up @@ -173,7 +185,8 @@ private boolean putInternal(Put put, Connection connection)
return true;
}
} else {
return new ConditionalMutator(put, tableMetadata, connection, queryBuilder).mutate();
return new ConditionalMutator(put, tableMetadata, connection, rdbEngine, queryBuilder)
.mutate();
}
}

Expand All @@ -199,7 +212,8 @@ private boolean deleteInternal(Delete delete, Connection connection)
return true;
}
} else {
return new ConditionalMutator(delete, tableMetadata, connection, queryBuilder).mutate();
return new ConditionalMutator(delete, tableMetadata, connection, rdbEngine, queryBuilder)
.mutate();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.scalar.db.storage.jdbc;

import com.scalar.db.common.error.CoreError;
import java.sql.Connection;
import java.sql.SQLException;

/** Factory class of subclasses of {@link RdbEngineStrategy} */
public final class RdbEngineFactory {
Expand All @@ -11,21 +9,14 @@ private RdbEngineFactory() {
}

public static RdbEngineStrategy create(JdbcConfig config) {
return create(config.getJdbcUrl());
}

public static RdbEngineStrategy create(Connection connection) throws SQLException {
String jdbcUrl = connection.getMetaData().getURL();
return create(jdbcUrl);
}
String jdbcUrl = config.getJdbcUrl();

static RdbEngineStrategy create(String jdbcUrl) {
if (jdbcUrl.startsWith("jdbc:mysql:")) {
return new RdbEngineMysql();
return new RdbEngineMysql(config);
} else if (jdbcUrl.startsWith("jdbc:postgresql:")) {
return new RdbEnginePostgresql();
} else if (jdbcUrl.startsWith("jdbc:oracle:")) {
return new RdbEngineOracle();
return new RdbEngineOracle(config);
} else if (jdbcUrl.startsWith("jdbc:sqlserver:")) {
return new RdbEngineSqlServer();
} else if (jdbcUrl.startsWith("jdbc:sqlite:")) {
Expand Down
15 changes: 13 additions & 2 deletions core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineMysql.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.scalar.db.storage.jdbc;

import com.google.common.annotations.VisibleForTesting;
import com.scalar.db.api.LikeExpression;
import com.scalar.db.api.TableMetadata;
import com.scalar.db.common.error.CoreError;
Expand All @@ -21,6 +22,16 @@

class RdbEngineMysql implements RdbEngineStrategy {
private static final Logger logger = LoggerFactory.getLogger(RdbEngineMysql.class);
private final String keyColumnSize;

RdbEngineMysql(JdbcConfig config) {
keyColumnSize = String.valueOf(config.getMysqlVariableKeyColumnSize());
}

@VisibleForTesting
RdbEngineMysql() {
keyColumnSize = String.valueOf(JdbcConfig.DEFAULT_VARIABLE_KEY_COLUMN_SIZE);
}

@Override
public String[] createSchemaSqls(String fullSchema) {
Expand Down Expand Up @@ -201,9 +212,9 @@ public String getDataTypeForEngine(DataType scalarDbDataType) {
public String getDataTypeForKey(DataType dataType) {
switch (dataType) {
case TEXT:
return "VARCHAR(64)";
return "VARCHAR(" + keyColumnSize + ")";
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this column definition is also used for coordinator.state.tx_id column, which usually contains a UUID value or a UUID value plus 25 byte prefix for the group commit feature. So, a validation that confirms the key column size is equal to or larger than 64 would be needed somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@komamitsu Thank you! That's a really good point. We should check it maybe in JdbcConfig. I will add it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@komamitsu Revised in b04a069. PTAL!

case BLOB:
return "VARBINARY(64)";
return "VARBINARY(" + keyColumnSize + ")";
default:
return null;
}
Expand Down
15 changes: 13 additions & 2 deletions core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineOracle.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static com.scalar.db.util.ScalarDbUtils.getFullTableName;

import com.google.common.annotations.VisibleForTesting;
import com.scalar.db.api.LikeExpression;
import com.scalar.db.api.TableMetadata;
import com.scalar.db.common.error.CoreError;
Expand All @@ -23,6 +24,16 @@

class RdbEngineOracle implements RdbEngineStrategy {
private static final Logger logger = LoggerFactory.getLogger(RdbEngineOracle.class);
private final String keyColumnSize;

RdbEngineOracle(JdbcConfig config) {
keyColumnSize = String.valueOf(config.getOracleVariableKeyColumnSize());
}

@VisibleForTesting
RdbEngineOracle() {
keyColumnSize = String.valueOf(JdbcConfig.DEFAULT_VARIABLE_KEY_COLUMN_SIZE);
}

@Override
public String[] createSchemaSqls(String fullSchema) {
Expand Down Expand Up @@ -208,9 +219,9 @@ public String getDataTypeForEngine(DataType scalarDbDataType) {
public String getDataTypeForKey(DataType dataType) {
switch (dataType) {
case TEXT:
return "VARCHAR2(64)";
return "VARCHAR2(" + keyColumnSize + ")";
case BLOB:
return "RAW(64)";
return "RAW(" + keyColumnSize + ")";
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ public String getDataTypeForEngine(DataType scalarDbDataType) {

@Override
public String getDataTypeForKey(DataType dataType) {
// The number 10485760 is due to the maximum length of the character column.
// https://www.postgresql.org/docs/15/datatype-character.html
Comment on lines +207 to +208
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

if (dataType == DataType.TEXT) {
return "VARCHAR(10485760)";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import com.scalar.db.storage.jdbc.JdbcUtils;
import com.scalar.db.storage.jdbc.RdbEngineFactory;
import com.scalar.db.storage.jdbc.RdbEngineStrategy;
import com.scalar.db.storage.jdbc.query.QueryBuilder;
import com.scalar.db.util.ThrowableFunction;
import java.sql.SQLException;
import java.util.List;
Expand Down Expand Up @@ -68,8 +67,7 @@ public JdbcTransactionManager(DatabaseConfig databaseConfig) {
databaseConfig.getMetadataCacheExpirationTimeSecs());

OperationChecker operationChecker = new OperationChecker(databaseConfig, tableMetadataManager);
QueryBuilder queryBuilder = new QueryBuilder(rdbEngine);
jdbcService = new JdbcService(tableMetadataManager, operationChecker, queryBuilder);
jdbcService = new JdbcService(tableMetadataManager, operationChecker, rdbEngine);
}

@VisibleForTesting
Expand Down
Loading
Loading