This is an automated email from the ASF dual-hosted git repository.

zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 428af0d11cc Add lock timeout in DistSQL LOCK CLUSTER WITH lockStrategy 
and UNLOCK CLUSTER (#34169)
428af0d11cc is described below

commit 428af0d11cc8442817bebbc1896987757509f36b
Author: cccchen <[email protected]>
AuthorDate: Fri Dec 27 14:09:09 2024 +0800

    Add lock timeout in DistSQL LOCK CLUSTER WITH lockStrategy and UNLOCK 
CLUSTER (#34169)
    
    * Add lock timeout in DistSQL
    
    * Modify the parameter name and type, fix checkstyle.
    
    * Update document of DistSQL
    
    * fix checkstyle
---
 .../distsql/syntax/ral/lock-cluster-with.cn.md     | 53 ++++++++++++++++++++++
 .../distsql/syntax/ral/lock-cluster-with.en.md     | 50 ++++++++++++++++++++
 .../distsql/syntax/ral/unlock-cluster.cn.md        | 52 +++++++++++++++++++++
 .../distsql/syntax/ral/unlock-cluster.en.md        | 48 ++++++++++++++++++++
 .../engine/src/main/antlr4/imports/Keyword.g4      |  4 ++
 .../engine/src/main/antlr4/imports/RALStatement.g4 |  4 +-
 .../core/kernel/KernelDistSQLStatementVisitor.java |  4 +-
 .../ral/updatable/LockClusterStatement.java        |  8 ++++
 .../ral/updatable/UnlockClusterStatement.java      |  8 ++++
 .../distsql/ral/updatable/LockClusterExecutor.java |  4 +-
 .../ral/updatable/UnlockClusterExecutor.java       |  4 +-
 .../ral/updatable/LockClusterExecutorTest.java     | 12 ++++-
 .../ral/updatable/UnlockClusterExecutorTest.java   | 11 ++++-
 13 files changed, 251 insertions(+), 11 deletions(-)

diff --git 
a/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/lock-cluster-with.cn.md
 
b/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/lock-cluster-with.cn.md
new file mode 100644
index 00000000000..ad51c5bafbb
--- /dev/null
+++ 
b/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/lock-cluster-with.cn.md
@@ -0,0 +1,53 @@
++++
+title = "LOCK CLUSTER WITH"
+weight = 16
++++
+
+### 描述
+
+`LOCK CLUSTER WITH` 语法用于向 `CLUSTER` 施加特定算法的锁。
+
+### 语法
+
+{{< tabs >}}
+{{% tab name="语法" %}}
+```sql
+LockClusterWith ::=
+  'LOCK' 'CLUSTER' 'WITH' lockStrategy ('TIMEOUT' timeoutMillis)?
+
+timeoutmillis ::=
+  long
+```
+{{% /tab %}}
+{{% tab name="铁路图" %}}
+<iframe frameborder="0" name="diagram" id="diagram" width="100%" 
height="100%"></iframe>
+{{% /tab %}}
+{{< /tabs >}}
+
+### 补充说明
+
+- 当 `CLUSTER` 已经处于被锁状态时,无法重复加锁,否则会抛出异常。
+- `lockStrategy` 当前支持两种锁策略,分别是排他锁 `WRITE` 与读写锁 `READ_WRITE`。
+- `timeoutMillis` 用于表明尝试加锁的超时时间,其单位为毫秒,未指定时,默认为 3000 毫秒。
+
+### 示例
+
+- 采用排他锁锁定 `CLUSTER` ,不设置超时时间
+
+```sql
+LOCK CLUSTER WITH WRITE;
+```
+
+- 采用读写锁锁定 `CLUSTER` ,并设置超时时间为 2000 毫秒
+
+```sql
+LOCK CLUSTER WITH READ_WRITE TIMEOUT 2000;
+```
+
+### 保留字
+
+`LOCK`、`CLUSTER`、`WITH`
+
+### 相关链接
+
+- [保留字](/cn/user-manual/shardingsphere-proxy/distsql/syntax/reserved-word/)
diff --git 
a/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/lock-cluster-with.en.md
 
b/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/lock-cluster-with.en.md
new file mode 100644
index 00000000000..469e0386488
--- /dev/null
+++ 
b/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/lock-cluster-with.en.md
@@ -0,0 +1,50 @@
++++
+title = "LOCK CLUSTER WITH"
+weight = 16
++++
+
+### Description
+
+The `LOCK CLUSTER WITH` syntax is utilized to apply a lock with a specific 
algorithm to the `CLUSTER`.
+
+### Syntax
+
+{{< tabs >}}
+{{% tab name="Grammar" %}}
+```sql
+LockClusterWith ::=
+  'LOCK' 'CLUSTER' 'WITH' lockStrategy ('TIMEOUT' timeoutMillis)?
+
+timeoutmillis ::=
+  long
+```
+
+{{% /tab %}}
+{{% tab name="Railroad diagram" %}}
+
+<iframe frameborder="0" name="diagram" id="diagram" width="100%" 
height="100%"></iframe>{{% /tab %}}{{< /tabs >}}
+
+### Supplement
+
+- When the `CLUSTER` is already locked, it is impossible to re-lock it, 
otherwise an exception will be thrown.
+- Currently, the `lockStrategy` supports two lock strategies, namely the 
exclusive lock `WRITE` and the read-write lock `READ_WRITE` .
+- The `timeoutMillis` is used to indicate the timeout period for attempting to 
acquire the lock, with the unit being milliseconds. When not specified, the 
default value is 3,000 milliseconds.
+
+### Example
+
+- Lock the `CLUSTER` with an exclusive lock without setting the timeout.
+```sql
+LOCK CLUSTER WITH WRITE;
+```
+- Lock the CLUSTER with a read-write lock and set the timeout to 2000 
milliseconds.
+```sql
+LOCK CLUSTER WITH READ_WRITE TIMEOUT 2000;
+```
+
+### Reserved words
+
+`LOCK`,`CLUSTER`,`WITH`
+
+### Related links
+
+- [Reserved 
word](/en/user-manual/shardingsphere-proxy/distsql/syntax/reserved-word/)
diff --git 
a/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/unlock-cluster.cn.md
 
b/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/unlock-cluster.cn.md
new file mode 100644
index 00000000000..4fb0e462d2a
--- /dev/null
+++ 
b/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/unlock-cluster.cn.md
@@ -0,0 +1,52 @@
++++
+title = "UNLOCK CLUSTER"
+weight = 17
++++
+
+### 描述
+
+`UNLOCK CLUSTER` 语法用于解除通过 `LOCK CLUSTER WITH` 语句施加在 `CLUSTER` 上的锁。
+
+### 语法
+
+{{< tabs >}}
+{{% tab name="语法" %}}
+```sql
+UnlockCluster ::=
+  'UNLOCK' 'CLUSTER' ('TIMEOUT' timeoutMillis)?
+
+timeoutmillis ::=
+  long
+```
+{{% /tab %}}
+{{% tab name="铁路图" %}}
+<iframe frameborder="0" name="diagram" id="diagram" width="100%" 
height="100%"></iframe>
+{{% /tab %}}
+{{< /tabs >}}
+
+### 补充说明
+
+- 当 `CLUSTER` 不处于被锁状态时,无法解除锁,否则会抛出异常。
+- `timeoutMillis` 表明尝试解锁的超时时间,其单位为毫秒,未指定时,默认为 3000 毫秒。
+
+### 示例
+
+- 解锁 `CLUSTER` ,不设置超时时间。
+
+```sql
+UNLOCK CLUSTER;
+```
+
+- 解锁 `CLUSTER` ,并设置超时时间为 2000 毫秒。
+
+```sql
+UNLOCK CLUSTER TIMEOUT 2000;
+```
+
+### 保留字
+
+`UNLOCK`、`CLUSTER`
+
+### 相关链接
+
+- [保留字](/cn/user-manual/shardingsphere-proxy/distsql/syntax/reserved-word/)
diff --git 
a/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/unlock-cluster.en.md
 
b/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/unlock-cluster.en.md
new file mode 100644
index 00000000000..b7114d80ccc
--- /dev/null
+++ 
b/docs/document/content/user-manual/shardingsphere-proxy/distsql/syntax/ral/unlock-cluster.en.md
@@ -0,0 +1,48 @@
++++
+title = "UNLOCK CLUSTER"
+weight = 17
++++
+
+### Description
+
+The `UNLOCK CLUSTER` syntax is used to release the lock applied to the 
`CLUSTER` by the `LOCK CLUSTER WITH` statement.
+
+### Syntax
+
+{{< tabs >}}
+{{% tab name="Grammar" %}}
+```sql
+UnlockCluster ::=
+  'UNLOCK' 'CLUSTER' ('TIMEOUT' timeoutMillis)?
+
+timeoutmillis ::=
+  long
+```
+
+{{% /tab %}}
+{{% tab name="Railroad diagram" %}}
+
+<iframe frameborder="0" name="diagram" id="diagram" width="100%" 
height="100%"></iframe>{{% /tab %}}{{< /tabs >}}
+
+### Supplement
+
+- When the `CLUSTER` is not in a locked state, it is impossible to release the 
lock; otherwise, an exception will be thrown.
+- `timeoutMillis` is used to indicate the timeout duration for attempting to 
unlock, with the unit being milliseconds. When not specified, the default value 
is 3,000 milliseconds.
+### Example
+
+- Unlock the `CLUSTER` without setting a timeout.
+```sql
+UNLOCK CLUSTER;
+```
+- Unlock the `CLUSTER` and set the timeout to 2000 milliseconds.
+```sql
+UNLOCK CLUSTER TIMEOUT 2000;
+```
+
+### Reserved words
+
+`UNLOCK`,`CLUSTER`
+
+### Related links
+
+- [Reserved 
word](/en/user-manual/shardingsphere-proxy/distsql/syntax/reserved-word/)
diff --git a/parser/distsql/engine/src/main/antlr4/imports/Keyword.g4 
b/parser/distsql/engine/src/main/antlr4/imports/Keyword.g4
index 310000c14ae..00e963e5536 100644
--- a/parser/distsql/engine/src/main/antlr4/imports/Keyword.g4
+++ b/parser/distsql/engine/src/main/antlr4/imports/Keyword.g4
@@ -342,3 +342,7 @@ FORCE
 CHECK_PRIVILEGES
     : C H E C K UL_ P R I V I L E G E S
     ;
+
+TIMEOUT
+    : T I M E O U T
+    ;
\ No newline at end of file
diff --git a/parser/distsql/engine/src/main/antlr4/imports/RALStatement.g4 
b/parser/distsql/engine/src/main/antlr4/imports/RALStatement.g4
index 5d9e356e55f..e5f63ffc209 100644
--- a/parser/distsql/engine/src/main/antlr4/imports/RALStatement.g4
+++ b/parser/distsql/engine/src/main/antlr4/imports/RALStatement.g4
@@ -100,11 +100,11 @@ convertYamlConfiguration
     ;
 
 lockCluster
-    : LOCK CLUSTER WITH lockStrategy
+    : LOCK CLUSTER WITH lockStrategy (TIMEOUT INT_)?
     ;
 
 unlockCluster
-    : UNLOCK CLUSTER
+    : UNLOCK CLUSTER (TIMEOUT INT_)?
     ;
 
 showPluginImplementations
diff --git 
a/parser/distsql/engine/src/main/java/org/apache/shardingsphere/distsql/parser/core/kernel/KernelDistSQLStatementVisitor.java
 
b/parser/distsql/engine/src/main/java/org/apache/shardingsphere/distsql/parser/core/kernel/KernelDistSQLStatementVisitor.java
index 82f3e5c973a..c725b1e642c 100644
--- 
a/parser/distsql/engine/src/main/java/org/apache/shardingsphere/distsql/parser/core/kernel/KernelDistSQLStatementVisitor.java
+++ 
b/parser/distsql/engine/src/main/java/org/apache/shardingsphere/distsql/parser/core/kernel/KernelDistSQLStatementVisitor.java
@@ -322,12 +322,12 @@ public final class KernelDistSQLStatementVisitor extends 
KernelDistSQLStatementB
     
     @Override
     public ASTNode visitLockCluster(final LockClusterContext ctx) {
-        return new LockClusterStatement((AlgorithmSegment) 
visitAlgorithmDefinition(ctx.lockStrategy().algorithmDefinition()));
+        return new LockClusterStatement((AlgorithmSegment) 
visitAlgorithmDefinition(ctx.lockStrategy().algorithmDefinition()), 
Long.parseLong(getIdentifierValue(ctx.INT_())));
     }
     
     @Override
     public ASTNode visitUnlockCluster(final UnlockClusterContext ctx) {
-        return new UnlockClusterStatement();
+        return new 
UnlockClusterStatement(Long.parseLong(getIdentifierValue(ctx.INT_())));
     }
     
     private String getIdentifierValue(final ParseTree context) {
diff --git 
a/parser/distsql/statement/src/main/java/org/apache/shardingsphere/distsql/statement/ral/updatable/LockClusterStatement.java
 
b/parser/distsql/statement/src/main/java/org/apache/shardingsphere/distsql/statement/ral/updatable/LockClusterStatement.java
index 215ab488578..04915e9038a 100644
--- 
a/parser/distsql/statement/src/main/java/org/apache/shardingsphere/distsql/statement/ral/updatable/LockClusterStatement.java
+++ 
b/parser/distsql/statement/src/main/java/org/apache/shardingsphere/distsql/statement/ral/updatable/LockClusterStatement.java
@@ -21,6 +21,8 @@ import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import org.apache.shardingsphere.distsql.segment.AlgorithmSegment;
 
+import java.util.Optional;
+
 /**
  * Lock cluster statement.
  */
@@ -29,4 +31,10 @@ import 
org.apache.shardingsphere.distsql.segment.AlgorithmSegment;
 public final class LockClusterStatement extends UpdatableRALStatement {
     
     private final AlgorithmSegment lockStrategy;
+    
+    private final Long timeoutMillis;
+    
+    public Optional<Long> getTimeoutMillis() {
+        return Optional.of(timeoutMillis);
+    }
 }
diff --git 
a/parser/distsql/statement/src/main/java/org/apache/shardingsphere/distsql/statement/ral/updatable/UnlockClusterStatement.java
 
b/parser/distsql/statement/src/main/java/org/apache/shardingsphere/distsql/statement/ral/updatable/UnlockClusterStatement.java
index 8f588abd89d..fe584b661af 100644
--- 
a/parser/distsql/statement/src/main/java/org/apache/shardingsphere/distsql/statement/ral/updatable/UnlockClusterStatement.java
+++ 
b/parser/distsql/statement/src/main/java/org/apache/shardingsphere/distsql/statement/ral/updatable/UnlockClusterStatement.java
@@ -19,9 +19,17 @@ package 
org.apache.shardingsphere.distsql.statement.ral.updatable;
 
 import lombok.RequiredArgsConstructor;
 
+import java.util.Optional;
+
 /**
  * Unlock cluster statement.
  */
 @RequiredArgsConstructor
 public final class UnlockClusterStatement extends UpdatableRALStatement {
+    
+    private final Long timeoutMillis;
+    
+    public Optional<Long> getTimeoutMillis() {
+        return Optional.of(timeoutMillis);
+    }
 }
diff --git 
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/LockClusterExecutor.java
 
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/LockClusterExecutor.java
index f490b58143a..00cd54cd9fe 100644
--- 
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/LockClusterExecutor.java
+++ 
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/LockClusterExecutor.java
@@ -44,8 +44,8 @@ public final class LockClusterExecutor implements 
DistSQLUpdateExecutor<LockClus
         checkAlgorithm(sqlStatement);
         LockContext lockContext = 
contextManager.getComputeNodeInstanceContext().getLockContext();
         GlobalLockDefinition lockDefinition = new GlobalLockDefinition(new 
ClusterLock());
-        // TODO should configured in SQL Statement
-        if (lockContext.tryLock(lockDefinition, 3000L)) {
+        long timeoutMillis = sqlStatement.getTimeoutMillis().orElse(3000L);
+        if (lockContext.tryLock(lockDefinition, timeoutMillis)) {
             try {
                 checkState(contextManager);
                 TypedSPILoader.getService(ClusterLockStrategy.class, 
sqlStatement.getLockStrategy().getName()).lock();
diff --git 
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/UnlockClusterExecutor.java
 
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/UnlockClusterExecutor.java
index 9b744e64956..94d992aaac8 100644
--- 
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/UnlockClusterExecutor.java
+++ 
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/UnlockClusterExecutor.java
@@ -39,8 +39,8 @@ public final class UnlockClusterExecutor implements 
DistSQLUpdateExecutor<Unlock
         checkState(contextManager);
         LockContext lockContext = 
contextManager.getComputeNodeInstanceContext().getLockContext();
         GlobalLockDefinition lockDefinition = new GlobalLockDefinition(new 
ClusterLock());
-        // TODO should configured in SQL Statement
-        if (lockContext.tryLock(lockDefinition, 3000L)) {
+        long timeoutMillis = sqlStatement.getTimeoutMillis().orElse(3000L);
+        if (lockContext.tryLock(lockDefinition, timeoutMillis)) {
             try {
                 checkState(contextManager);
                 
contextManager.getPersistServiceFacade().getStatePersistService().update(ClusterState.OK);
diff --git 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/LockClusterExecutorTest.java
 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/LockClusterExecutorTest.java
index 00e5083fc0e..4fae2b0b407 100644
--- 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/LockClusterExecutorTest.java
+++ 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/LockClusterExecutorTest.java
@@ -32,6 +32,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import java.util.Properties;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -46,13 +47,20 @@ class LockClusterExecutorTest {
     void assertExecuteUpdateWithLockedCluster() {
         ContextManager contextManager = mock(ContextManager.class, 
RETURNS_DEEP_STUBS);
         
when(contextManager.getStateContext().getClusterState()).thenReturn(ClusterState.UNAVAILABLE);
-        assertThrows(LockedClusterException.class, () -> 
executor.executeUpdate(new LockClusterStatement(new AlgorithmSegment("FOO", new 
Properties())), contextManager));
+        assertThrows(LockedClusterException.class, () -> 
executor.executeUpdate(new LockClusterStatement(new AlgorithmSegment("FOO", new 
Properties()), null), contextManager));
     }
     
     @Test
     void assertExecuteUpdateWithWrongAlgorithm() {
         ContextManager contextManager = mock(ContextManager.class, 
RETURNS_DEEP_STUBS);
         
when(contextManager.getStateContext().getClusterState()).thenReturn(ClusterState.OK);
-        assertThrows(ServiceProviderNotFoundException.class, () -> 
executor.executeUpdate(new LockClusterStatement(new AlgorithmSegment("FOO", new 
Properties())), contextManager));
+        assertThrows(ServiceProviderNotFoundException.class, () -> 
executor.executeUpdate(new LockClusterStatement(new AlgorithmSegment("FOO", new 
Properties()), null), contextManager));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithUsingTimeout() {
+        ContextManager contextManager = mock(ContextManager.class, 
RETURNS_DEEP_STUBS);
+        
when(contextManager.getStateContext().getClusterState()).thenReturn(ClusterState.OK);
+        assertDoesNotThrow(() -> executor.executeUpdate(new 
LockClusterStatement(new AlgorithmSegment("WRITE", new Properties()), 2000L), 
contextManager));
     }
 }
diff --git 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/UnlockClusterExecutorTest.java
 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/UnlockClusterExecutorTest.java
index 02edc049cd9..076ff500c77 100644
--- 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/UnlockClusterExecutorTest.java
+++ 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/ral/updatable/UnlockClusterExecutorTest.java
@@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -43,6 +44,14 @@ class UnlockClusterExecutorTest {
         ContextManager contextManager = mock(ContextManager.class, 
RETURNS_DEEP_STUBS);
         
when(contextManager.getStateContext().getClusterState()).thenReturn(ClusterState.OK);
         
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
-        assertThrows(NotLockedClusterException.class, () -> 
executor.executeUpdate(new UnlockClusterStatement(), contextManager));
+        assertThrows(NotLockedClusterException.class, () -> 
executor.executeUpdate(new UnlockClusterStatement(null), contextManager));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithUsingTimeout() {
+        ContextManager contextManager = mock(ContextManager.class, 
RETURNS_DEEP_STUBS);
+        
when(contextManager.getStateContext().getClusterState()).thenReturn(ClusterState.UNAVAILABLE);
+        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        assertDoesNotThrow(() -> executor.executeUpdate(new 
UnlockClusterStatement(2000L), contextManager));
     }
 }

Reply via email to