Skip to content

Commit d801e8f

Browse files
committed
Add support for ignore lock wait timeout
1 parent 9cd2417 commit d801e8f

File tree

3 files changed

+121
-69
lines changed

3 files changed

+121
-69
lines changed

r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/ConnectionContext.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ public final class ConnectionContext implements CodecContext {
5353
@Nullable
5454
private ZoneId timeZone;
5555

56+
private boolean lockWaitTimeoutSupported = false;
57+
5658
/**
5759
* Assume that the auto commit is always turned on, it will be set after handshake V10 request message, or
5860
* OK message which means handshake V9 completed.
@@ -162,6 +164,22 @@ public int getLocalInfileBufferSize() {
162164
return localInfileBufferSize;
163165
}
164166

167+
/**
168+
* Checks if the server supports lock wait timeout.
169+
*
170+
* @return if the server supports lock wait timeout.
171+
*/
172+
public boolean isLockWaitTimeoutSupported() {
173+
return lockWaitTimeoutSupported;
174+
}
175+
176+
/**
177+
* Enables lock wait timeout supported when loading session variables.
178+
*/
179+
public void enableLockWaitTimeoutSupported() {
180+
this.lockWaitTimeoutSupported = true;
181+
}
182+
165183
/**
166184
* Get the bitmap of server statuses.
167185
*

r2dbc-mysql/src/main/java/io/asyncer/r2dbc/mysql/MySqlSimpleConnection.java

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ public Mono<Void> beginTransaction() {
205205

206206
@Override
207207
public Mono<Void> beginTransaction(TransactionDefinition definition) {
208-
return Mono.defer(() -> QueryFlow.beginTransaction(client, this, batchSupported, definition));
208+
return Mono.defer(() -> QueryFlow.beginTransaction(client, this, batchSupported, definition, context));
209209
}
210210

211211
@Override
@@ -306,8 +306,8 @@ public MySqlConnectionMetadata getMetadata() {
306306
}
307307

308308
/**
309-
* MySQL does not have any way to query the isolation level of the current transaction, only inferred from
310-
* past statements, so driver can not make sure the result is right.
309+
* MySQL does not have any way to query the isolation level of the current transaction, only inferred from past
310+
* statements, so driver can not make sure the result is right.
311311
* <p>
312312
* See <a href="https://bugs.mysql.com/bug.php?id=53341">MySQL Bug 53341</a>
313313
* <p>
@@ -424,6 +424,11 @@ public boolean isInTransaction() {
424424
public Mono<Void> setLockWaitTimeout(Duration timeout) {
425425
requireNonNull(timeout, "timeout must not be null");
426426

427+
if (!context.isLockWaitTimeoutSupported()) {
428+
logger.warn("Lock wait timeout is not supported by server, setLockWaitTimeout operation is ignored");
429+
return Mono.empty();
430+
}
431+
427432
long timeoutSeconds = timeout.getSeconds();
428433
return QueryFlow.executeVoid(client, "SET innodb_lock_wait_timeout=" + timeoutSeconds)
429434
.doOnSuccess(ignored -> this.lockWaitTimeout = this.currentLockWaitTimeout = timeoutSeconds);
@@ -484,13 +489,20 @@ static Mono<MySqlConnection> init(
484489
) {
485490
Mono<MySqlConnection> connection = initSessionVariables(client, sessionVariables)
486491
.then(loadSessionVariables(client, codecs, context))
492+
.flatMap(data -> loadInnoDbEngineStatus(data, client, codecs, context))
487493
.map(data -> {
488494
ZoneId timeZone = data.timeZone;
489495
if (timeZone != null) {
490496
logger.debug("Got server time zone {} from loading session variables", timeZone);
491497
context.setTimeZone(timeZone);
492498
}
493499

500+
if (data.lockWaitTimeoutSupported) {
501+
context.enableLockWaitTimeoutSupported();
502+
} else {
503+
logger.info("Lock wait timeout is not supported by server, all related operations will be ignored");
504+
}
505+
494506
return new MySqlSimpleConnection(client, context, codecs, data.level, data.lockWaitTimeout,
495507
queryCache, prepareCache, data.product, prepare);
496508
});
@@ -534,10 +546,10 @@ private static Mono<Void> initSessionVariables(Client client, List<String> sessi
534546
private static Mono<SessionData> loadSessionVariables(
535547
Client client, Codecs codecs, ConnectionContext context
536548
) {
537-
StringBuilder query = new StringBuilder(160)
549+
StringBuilder query = new StringBuilder(128)
538550
.append("SELECT ")
539551
.append(transactionIsolationColumn(context))
540-
.append(",@@innodb_lock_wait_timeout AS l,@@version_comment AS v");
552+
.append(",@@version_comment AS v");
541553

542554
Function<MySqlResult, Flux<SessionData>> handler;
543555

@@ -554,6 +566,24 @@ private static Mono<SessionData> loadSessionVariables(
554566
.last();
555567
}
556568

569+
private static Mono<SessionData> loadInnoDbEngineStatus(
570+
SessionData data, Client client, Codecs codecs, ConnectionContext context
571+
) {
572+
return new TextSimpleStatement(client, codecs, context,
573+
"SHOW VARIABLES LIKE 'innodb\\\\_lock\\\\_wait\\\\_timeout'")
574+
.execute()
575+
.flatMap(r -> r.map(readable -> {
576+
String value = readable.get(1, String.class);
577+
578+
if (value == null || value.isEmpty()) {
579+
return data;
580+
} else {
581+
return data.lockWaitTimeout(Long.parseLong(value));
582+
}
583+
}))
584+
.single(data);
585+
}
586+
557587
private static Mono<Void> initDatabase(Client client, String database) {
558588
return client.exchange(new InitDbMessage(database), INIT_DB)
559589
.last()
@@ -572,16 +602,15 @@ private static Mono<Void> initDatabase(Client client, String database) {
572602
private static Flux<SessionData> convertSessionData(MySqlResult r, boolean timeZone) {
573603
return r.map(readable -> {
574604
IsolationLevel level = convertIsolationLevel(readable.get(0, String.class));
575-
long lockWaitTimeout = convertLockWaitTimeout(readable.get(1, Long.class));
576-
String product = readable.get(2, String.class);
605+
String product = readable.get(1, String.class);
577606

578-
return new SessionData(level, lockWaitTimeout, product, timeZone ? readZoneId(readable) : null);
607+
return new SessionData(level, product, timeZone ? readZoneId(readable) : null);
579608
});
580609
}
581610

582611
private static ZoneId readZoneId(Readable readable) {
583-
String systemTimeZone = readable.get(3, String.class);
584-
String timeZone = readable.get(4, String.class);
612+
String systemTimeZone = readable.get(2, String.class);
613+
String timeZone = readable.get(3, String.class);
585614

586615
if (timeZone == null || timeZone.isEmpty() || "SYSTEM".equalsIgnoreCase(timeZone)) {
587616
if (systemTimeZone == null || systemTimeZone.isEmpty()) {
@@ -628,24 +657,13 @@ private static IsolationLevel convertIsolationLevel(@Nullable String name) {
628657
return IsolationLevel.REPEATABLE_READ;
629658
}
630659

631-
private static long convertLockWaitTimeout(@Nullable Long timeout) {
632-
if (timeout == null) {
633-
logger.error("Lock wait timeout is null, fallback to " + DEFAULT_LOCK_WAIT_TIMEOUT + " seconds");
634-
635-
return DEFAULT_LOCK_WAIT_TIMEOUT;
636-
}
637-
638-
return timeout;
639-
}
640-
641660
/**
642-
* Resolves the column of session isolation level, the {@literal @@tx_isolation} has been marked as
643-
* deprecated.
661+
* Resolves the column of session isolation level, the {@literal @@tx_isolation} has been marked as deprecated.
644662
* <p>
645663
* If server is MariaDB, {@literal @@transaction_isolation} is used starting from {@literal 11.1.1}.
646664
* <p>
647-
* If the server is MySQL, use {@literal @@transaction_isolation} starting from {@literal 8.0.3}, or
648-
* between {@literal 5.7.20} and {@literal 8.0.0} (exclusive).
665+
* If the server is MySQL, use {@literal @@transaction_isolation} starting from {@literal 8.0.3}, or between
666+
* {@literal 5.7.20} and {@literal 8.0.0} (exclusive).
649667
*/
650668
private static String transactionIsolationColumn(ConnectionContext context) {
651669
ServerVersion version = context.getServerVersion();
@@ -664,20 +682,26 @@ private static final class SessionData {
664682

665683
private final IsolationLevel level;
666684

667-
private final long lockWaitTimeout;
668-
669685
@Nullable
670686
private final String product;
671687

672688
@Nullable
673689
private final ZoneId timeZone;
674690

675-
private SessionData(IsolationLevel level, long lockWaitTimeout, @Nullable String product,
676-
@Nullable ZoneId timeZone) {
691+
private long lockWaitTimeout = -1;
692+
693+
private boolean lockWaitTimeoutSupported;
694+
695+
private SessionData(IsolationLevel level, @Nullable String product, @Nullable ZoneId timeZone) {
677696
this.level = level;
678-
this.lockWaitTimeout = lockWaitTimeout;
679697
this.product = product;
680698
this.timeZone = timeZone;
681699
}
700+
701+
SessionData lockWaitTimeout(long timeout) {
702+
this.lockWaitTimeoutSupported = true;
703+
this.lockWaitTimeout = timeout;
704+
return this;
705+
}
682706
}
683707
}

0 commit comments

Comments
 (0)