Skip to content

Commit c7955f6

Browse files
authored
[Feature][Server] Server Support postgre (#583)
1 parent 1360764 commit c7955f6

25 files changed

Lines changed: 1940 additions & 110 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ Need: Maven 3.6.1 and later
3232
```sh
3333
$ mvn clean package -Prelease -DskipTests
3434
```
35+
36+
## Metadata Database
37+
38+
- The default `datavines-server` configuration uses `PostgreSQL`
39+
- Initialize a PostgreSQL metadata database with `scripts/sql/datavines-postgresql.sql`
40+
- If you want to run with `MySQL`, switch to the `mysql` Spring profile and initialize with `scripts/sql/datavines-mysql.sql`
41+
- The release package now contains both scripts in `${DATAVINES_HOME}/scripts/`
42+
3543
## Features
3644

3745
### Data Catalog

bin/datavines-daemon.sh

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# limitations under the License.
1717
#
1818

19-
usage="Usage: datavines-daemon.sh (start|start_container|start_with_jmx|stop|restart_with_jmx|status) <''|mysql>"
19+
usage="Usage: datavines-daemon.sh (start|start_container|start_with_jmx|stop|restart_with_jmx|status) <''|postgres|mysql>"
2020

2121
# if no args specified, show usage
2222
if [ $# -le 0 ]; then
@@ -32,12 +32,12 @@ shift
3232
springProfileActive=
3333

3434
if [ -n "$profile" ]; then
35-
if [ "$profile" = "mysql" ]; then
36-
springProfileActive="-Dspring.profiles.active=mysql"
37-
else
38-
echo "Error: No profile named \`$profile' was found."
39-
exit 1
40-
fi
35+
if [ "$profile" = "postgres" ] || [ "$profile" = "mysql" ]; then
36+
springProfileActive="-Dspring.profiles.active=$profile"
37+
else
38+
echo "Error: No profile named \`$profile' was found."
39+
exit 1
40+
fi
4141
fi
4242

4343
echo "Begin $startStop DataVinesServer $profile......"

datavines-dist/src/main/assembly/datavines-bin.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
<directory>${basedir}/../scripts/sql</directory>
8181
<includes>
8282
<include>datavines-mysql.sql</include>
83+
<include>datavines-postgresql.sql</include>
8384
</includes>
8485
<outputDirectory>./scripts</outputDirectory>
8586
</fileSet>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Licensed to the Apache Software Foundation (ASF) under one or more
5+
contributor license agreements. See the NOTICE file distributed with
6+
this work for additional information regarding copyright ownership.
7+
The ASF licenses this file to You under the Apache License, Version 2.0
8+
(the "License"); you may not use this file except in compliance with
9+
the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
19+
-->
20+
21+
<project xmlns="http://maven.apache.org/POM/4.0.0"
22+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
23+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
24+
<parent>
25+
<artifactId>datavines-registry-plugins</artifactId>
26+
<groupId>io.datavines</groupId>
27+
<version>1.0.0-SNAPSHOT</version>
28+
</parent>
29+
<modelVersion>4.0.0</modelVersion>
30+
31+
<artifactId>datavines-registry-postgresql</artifactId>
32+
33+
<dependencies>
34+
<dependency>
35+
<groupId>com.zaxxer</groupId>
36+
<artifactId>HikariCP</artifactId>
37+
</dependency>
38+
</dependencies>
39+
40+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package io.datavines.registry.plugin;
18+
19+
import com.google.common.util.concurrent.ThreadFactoryBuilder;
20+
import io.datavines.common.utils.ConnectionUtils;
21+
import io.datavines.common.utils.NetUtils;
22+
import io.datavines.common.utils.ThreadUtils;
23+
import io.datavines.registry.api.ServerInfo;
24+
import lombok.AccessLevel;
25+
import lombok.RequiredArgsConstructor;
26+
import lombok.extern.slf4j.Slf4j;
27+
28+
import java.sql.Connection;
29+
import java.sql.PreparedStatement;
30+
import java.sql.SQLException;
31+
import java.sql.Timestamp;
32+
import java.util.ArrayList;
33+
import java.util.List;
34+
import java.util.Map;
35+
import java.util.Properties;
36+
import java.util.concurrent.ConcurrentHashMap;
37+
import java.util.concurrent.Executors;
38+
import java.util.concurrent.ScheduledExecutorService;
39+
import java.util.concurrent.TimeUnit;
40+
41+
@Slf4j
42+
public class PostgresqlMutex {
43+
44+
public static final long LOCK_ACQUIRE_INTERVAL = 1000;
45+
46+
private final long expireTimeWindow = 600;
47+
48+
private Connection connection;
49+
50+
private final Properties properties;
51+
52+
private final ServerInfo serverInfo;
53+
54+
private final ConcurrentHashMap<String, RegistryLock> lockHoldMap;
55+
56+
public PostgresqlMutex(Connection connection, Properties properties) throws SQLException {
57+
this.connection = connection;
58+
this.properties = properties;
59+
this.serverInfo = new ServerInfo(NetUtils.getHost(), Integer.valueOf((String) properties.get("server.port")));
60+
this.lockHoldMap = new ConcurrentHashMap<>();
61+
ScheduledExecutorService lockTermUpdateThreadPool = Executors.newSingleThreadScheduledExecutor(
62+
new ThreadFactoryBuilder().setNameFormat("RegistryLockRefreshThread").setDaemon(true).build());
63+
64+
lockTermUpdateThreadPool.scheduleWithFixedDelay(
65+
new LockTermRefreshTask(lockHoldMap),
66+
2,
67+
2,
68+
TimeUnit.SECONDS);
69+
70+
clearExpireLock();
71+
}
72+
73+
public boolean acquire(String lockKey, long time) {
74+
RegistryLock lock = lockHoldMap.computeIfAbsent(lockKey, key -> {
75+
RegistryLock registryLock = null;
76+
int count = 1;
77+
if (time > 0) {
78+
count = Math.max(1, (int) (time * 1000 / LOCK_ACQUIRE_INTERVAL));
79+
}
80+
while (count > 0) {
81+
try {
82+
registryLock = executeInsert(key);
83+
log.debug("Acquire the lock success, {}", key);
84+
count = 0;
85+
} catch (SQLException e) {
86+
log.error("Acquire the lock error, {}, try again!", e.getLocalizedMessage());
87+
try {
88+
clearExpireLock();
89+
} catch (SQLException ex) {
90+
log.error("clear expire lock error : ", ex);
91+
}
92+
ThreadUtils.sleep(LOCK_ACQUIRE_INTERVAL);
93+
count--;
94+
}
95+
}
96+
97+
return registryLock;
98+
});
99+
100+
return lock != null;
101+
}
102+
103+
public boolean release(String lockKey) throws SQLException {
104+
RegistryLock registryLock = lockHoldMap.get(lockKey);
105+
if (registryLock != null) {
106+
try {
107+
executeDelete(lockKey);
108+
lockHoldMap.remove(lockKey);
109+
} catch (SQLException e) {
110+
log.error(String.format("Release lock: %s error", lockKey), e);
111+
return false;
112+
}
113+
}
114+
115+
return true;
116+
}
117+
118+
public void close() throws SQLException {
119+
if (connection != null) {
120+
connection.close();
121+
}
122+
}
123+
124+
private RegistryLock executeInsert(String key) throws SQLException {
125+
checkConnection();
126+
Timestamp updateTime = new Timestamp(System.currentTimeMillis());
127+
PreparedStatement preparedStatement = connection.prepareStatement(
128+
"insert into dv_registry_lock (lock_key,lock_owner,update_time) values (?,?,?)");
129+
preparedStatement.setString(1, key);
130+
preparedStatement.setString(2, this.serverInfo.getAddr());
131+
preparedStatement.setTimestamp(3, updateTime);
132+
preparedStatement.executeUpdate();
133+
preparedStatement.close();
134+
return new RegistryLock(key, this.serverInfo.getAddr(), updateTime);
135+
}
136+
137+
private RegistryLock executeUpdate(String key) throws SQLException {
138+
checkConnection();
139+
Timestamp updateTime = new Timestamp(System.currentTimeMillis());
140+
PreparedStatement preparedStatement = connection.prepareStatement(
141+
"update dv_registry_lock set update_time = ? where lock_key = ?");
142+
preparedStatement.setTimestamp(1, updateTime);
143+
preparedStatement.setString(2, key);
144+
preparedStatement.executeUpdate();
145+
preparedStatement.close();
146+
return new RegistryLock(key, this.serverInfo.getAddr(), updateTime);
147+
}
148+
149+
private void executeDelete(String key) throws SQLException {
150+
checkConnection();
151+
PreparedStatement preparedStatement = connection.prepareStatement(
152+
"delete from dv_registry_lock where lock_key = ?");
153+
preparedStatement.setString(1, key);
154+
if (preparedStatement.executeUpdate() > 0) {
155+
lockHoldMap.remove(key);
156+
}
157+
preparedStatement.close();
158+
}
159+
160+
private void clearExpireLock() throws SQLException {
161+
checkConnection();
162+
PreparedStatement preparedStatement = connection.prepareStatement(
163+
"delete from dv_registry_lock where update_time < ?");
164+
preparedStatement.setTimestamp(1, new Timestamp(System.currentTimeMillis() - expireTimeWindow));
165+
preparedStatement.executeUpdate();
166+
preparedStatement.close();
167+
lockHoldMap.values().removeIf((v -> v.getUpdateTime().getTime() < (System.currentTimeMillis() - expireTimeWindow)));
168+
}
169+
170+
private void checkConnection() throws SQLException {
171+
if (connection == null || connection.isClosed()) {
172+
connection = ConnectionUtils.getConnection(properties);
173+
}
174+
}
175+
176+
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
177+
class LockTermRefreshTask implements Runnable {
178+
179+
private final Map<String, RegistryLock> lockHoldMap;
180+
181+
@Override
182+
public void run() {
183+
try {
184+
if (lockHoldMap.isEmpty()) {
185+
return;
186+
}
187+
188+
List<String> lockKeys = new ArrayList<>();
189+
for (RegistryLock lock : lockHoldMap.values()) {
190+
if (lock != null) {
191+
lockKeys.add(lock.getLockKey());
192+
}
193+
}
194+
lockKeys.forEach(lockKey -> {
195+
try {
196+
RegistryLock registryLock = executeUpdate(lockKey);
197+
lockHoldMap.put(lockKey, registryLock);
198+
} catch (SQLException e) {
199+
log.warn("Update the lock: {} term failed.", lockKey);
200+
}
201+
});
202+
203+
clearExpireLock();
204+
} catch (Exception e) {
205+
log.error("Update lock term error", e);
206+
}
207+
}
208+
}
209+
}

0 commit comments

Comments
 (0)