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

fix existDatabase(), clearBackend() and rollback() for postgresql #531

Merged
merged 11 commits into from
Jun 12, 2019
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,19 @@

public class HugeFactory {

private static final String NAME_REGEX = "^[A-Za-z][A-Za-z0-9_]{0,47}$";

private static final Map<String, HugeGraph> graphs = new HashMap<>();

public static synchronized HugeGraph open(Configuration config) {
HugeConfig conf = new HugeConfig(config);
String name = conf.get(CoreOptions.STORE);
E.checkArgument(name.matches(NAME_REGEX),
"Invalid graph name '%s', valid graph name is up to " +
"48 alpha-numeric characters and underscores " +
"and only letters are supported as first letter. " +
"Note: letter is case insensitive");
name = name.toLowerCase();
HugeGraph graph = graphs.get(name);
if (graph == null || graph.closed()) {
graph = new HugeGraph(conf);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public static synchronized CoreOptions instance() {

public static final ConfigOption<Boolean> VERTEX_CHECK_CUSTOMIZED_ID_EXIST =
new ConfigOption<>(
"vertex.check_customzied_id_exist",
"vertex.check_customized_id_exist",
"Whether to check the vertices exist for those using " +
"customized id strategy",
disallowEmpty(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public String database() {
return this.database;
}

public String escapedDatabase() {
return MysqlUtil.escapeString(this.database());
}

/**
* Try connect with specified database, will not reconnect if failed
* @throws SQLException if a database access error occurs
Expand All @@ -86,9 +90,9 @@ protected boolean opened() {
private Connection open(boolean autoReconnect) throws SQLException {
String url = this.config.get(MysqlOptions.JDBC_URL);
if (url.endsWith("/")) {
url = String.format("%s%s", url, this.database);
url = String.format("%s%s", url, this.database());
} else {
url = String.format("%s/%s", url, this.database);
url = String.format("%s/%s", url, this.database());
}

int maxTimes = this.config.get(MysqlOptions.JDBC_RECONNECT_MAX_TIMES);
Expand Down Expand Up @@ -146,15 +150,15 @@ public void checkSessionConnected() {

public void createDatabase() {
// Create database with non-database-session
LOG.debug("Create database: {}", this.database);
LOG.debug("Create database: {}", this.database());

String sql = this.buildCreateDatabase(this.database);
String sql = this.buildCreateDatabase(this.database());
try (Connection conn = this.openWithoutDB(0)) {
conn.createStatement().execute(sql);
} catch (SQLException e) {
if (!e.getMessage().endsWith("already exists")) {
throw new BackendException("Failed to create database '%s'", e,
this.database);
this.database());
}
// Ignore exception if database already exists
}
Expand All @@ -167,28 +171,31 @@ protected String buildCreateDatabase(String database) {
}

public void dropDatabase() {
LOG.debug("Drop database: {}", this.database);
LOG.debug("Drop database: {}", this.database());

String sql = String.format("DROP DATABASE IF EXISTS %s;",
this.database);
String sql = this.buildDropDatabase(this.database());
try (Connection conn = this.openWithoutDB(DROP_DB_TIMEOUT)) {
conn.createStatement().execute(sql);
} catch (SQLException e) {
if (e.getCause() instanceof SocketTimeoutException) {
LOG.warn("Drop database '{}' timeout", this.database);
LOG.warn("Drop database '{}' timeout", this.database());
} else {
throw new BackendException("Failed to drop database '%s'",
this.database);
this.database());
}
}
}

protected String buildDropDatabase(String database) {
return String.format("DROP DATABASE IF EXISTS %s;", database);
}

public boolean existsDatabase() {
try (Connection conn = this.openWithoutDB(0);
ResultSet result = conn.getMetaData().getCatalogs()) {
while (result.next()) {
String dbName = result.getString(1);
if (dbName.equals(this.database)) {
if (dbName.equals(this.database())) {
return true;
}
}
Expand Down Expand Up @@ -319,11 +326,10 @@ public Integer commit() {
this.clear();
} catch (SQLException e) {
throw new BackendException("Failed to commit", e);
} finally {
try {
this.end();
} catch (SQLException ignored) {}
}
try {
this.end();
} catch (SQLException ignored) {}
return updated;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package com.baidu.hugegraph.backend.store.postgresql;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.http.client.utils.URIBuilder;
Expand All @@ -45,6 +46,20 @@ public PostgresqlSessions(HugeConfig config, String database, String store) {
super(config, database, store);
}

@Override
public boolean existsDatabase() {
String statement = String.format(
"SELECT datname FROM pg_catalog.pg_database " +
"WHERE datname = %s;", this.escapedDatabase());
try (Connection conn = this.openWithoutDB(0)) {
ResultSet result = conn.createStatement().executeQuery(statement);
return result.next();
} catch (Exception e) {
throw new BackendException("Failed to obtain PostgreSQL metadata," +
" please ensure it is ok", e);
}
}

@Override
public void createDatabase() {
// Create database with non-database-session
Expand Down Expand Up @@ -76,6 +91,17 @@ protected String buildCreateDatabase(String database) {
return String.format(POSTGRESQL_DB_CREATE, database);
}

@Override
protected String buildDropDatabase(String database) {
return String.format(
"REVOKE CONNECT ON DATABASE %s FROM public;" +
"SELECT pg_terminate_backend(pg_stat_activity.pid) " +
" FROM pg_stat_activity " +
" WHERE pg_stat_activity.datname = '%s';" +
zhoney marked this conversation as resolved.
Show resolved Hide resolved
"DROP DATABASE IF EXISTS %s;",
database, database, database);
}

@Override
protected URIBuilder newConnectionURIBuilder() {
// Suppress error log when database does not exist
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
EdgeCoreTest.class,
VertexPropertyCoreTest.class,
EdgePropertyCoreTest.class,
RestoreCoreTest.class
RestoreCoreTest.class,
MultiGraphsTest.class
})
public class CoreTestSuite {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* Copyright 2017 HugeGraph Authors
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

package com.baidu.hugegraph.core;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.util.GraphFactory;
import org.junit.Test;

import com.baidu.hugegraph.HugeGraph;
import com.baidu.hugegraph.backend.id.IdGenerator;
import com.baidu.hugegraph.config.CoreOptions;
import com.baidu.hugegraph.testutil.Assert;
import com.baidu.hugegraph.testutil.Utils;

import jersey.repackaged.com.google.common.collect.ImmutableList;

public class MultiGraphsTest {

private static final String NAME48 =
"g12345678901234567890123456789012345678901234567";
@Test
zhoney marked this conversation as resolved.
Show resolved Hide resolved
public void testCreateMultiGraphs() {
List<HugeGraph> graphs = openGraphs("g1", "g_2", NAME48);
for (HugeGraph graph : graphs) {
graph.initBackend();
graph.clearBackend();
}
destoryGraphs(graphs);
}

@Test
public void testCreateGraphsWithInvalidNames() {
Assert.assertThrows(RuntimeException.class,
() -> openGraphs(""));
List<String> invalidNames = ImmutableList.of(
"123", " g", "g 1", " .", ". .",
"@$%^&*()_+`-={}|[]\"<?;'~,./\\",
" ~", "g~", "g'", "_1", "_a",
"1a", NAME48 + "8");
for (String name : invalidNames) {
Assert.assertThrows(RuntimeException.class, () -> openGraphs(name));
}
}

@Test
public void testCreateGraphsWithSameName() {
List<HugeGraph> graphs = openGraphs("g", "g", "G");
HugeGraph g1 = graphs.get(0);
HugeGraph g2 = graphs.get(1);
HugeGraph g3 = graphs.get(2);

g1.initBackend();
g2.initBackend();
g3.initBackend();

Assert.assertThrows(IllegalArgumentException.class,
() -> g2.vertexLabel("node"));
Assert.assertThrows(IllegalArgumentException.class,
() -> g3.vertexLabel("node"));
g1.schema().vertexLabel("node").useCustomizeNumberId()
.ifNotExist().create();
g2.vertexLabel("node");
g3.vertexLabel("node");

g1.addVertex(T.label, "node", T.id, 1);
g1.tx().commit();
Iterator<Vertex> vertices = g2.vertices(1);
Assert.assertTrue(vertices.hasNext());
Vertex vertex = vertices.next();
Assert.assertFalse(vertices.hasNext());
Assert.assertEquals(IdGenerator.of(1), vertex.id());

vertices = g3.vertices(1);
Assert.assertTrue(vertices.hasNext());
vertex = vertices.next();
Assert.assertFalse(vertices.hasNext());
Assert.assertEquals(IdGenerator.of(1), vertex.id());

g1.clearBackend();
g2.clearBackend();
g3.clearBackend();
destoryGraphs(ImmutableList.of(g1));
}

@Test
public void testCreateGraphWithSameNameDifferentBackends() {
HugeGraph g1 = openGraphWithBackend("graph", "memory", "text");
g1.initBackend();
Assert.assertThrows(RuntimeException.class,
() -> openGraphWithBackend("graph", "rocksdb",
"binary"));
g1.clearBackend();
g1.close();
}

@Test
public void testCreateGraphsWithDifferentNameDifferentBackends() {
HugeGraph g1 = openGraphWithBackend("g1", "memory", "text");
HugeGraph g2 = openGraphWithBackend("g2", "rocksdb", "binary");
HugeGraph graph = openGraphs("graph").get(0);
g1.initBackend();
g2.initBackend();
graph.initBackend();
g1.clearBackend();
zhoney marked this conversation as resolved.
Show resolved Hide resolved
g2.clearBackend();
graph.clearBackend();
destoryGraphs(ImmutableList.of(g1, g2, graph));
zhoney marked this conversation as resolved.
Show resolved Hide resolved
}

public static List<HugeGraph> openGraphs(String... graphNames) {
List<HugeGraph> graphs = new ArrayList<>(graphNames.length);
PropertiesConfiguration conf = Utils.getConf();
Configuration config = new BaseConfiguration();
for (Iterator<String> keys = conf.getKeys(); keys.hasNext();) {
String key = keys.next();
config.setProperty(key, conf.getProperty(key));
}
((BaseConfiguration) config).setDelimiterParsingDisabled(true);
for (String graphName : graphNames) {
config.setProperty(CoreOptions.STORE.name(), graphName);
graphs.add((HugeGraph) GraphFactory.open(config));
}
return graphs;
}

public static void destoryGraphs(List<HugeGraph> graphs) {
for (HugeGraph graph : graphs) {
graph.close();
}
}

public static HugeGraph openGraphWithBackend(String graph, String backend,
String serializer) {
PropertiesConfiguration conf = Utils.getConf();
Configuration config = new BaseConfiguration();
for (Iterator<String> keys = conf.getKeys(); keys.hasNext();) {
String key = keys.next();
config.setProperty(key, conf.getProperty(key));
}
((BaseConfiguration) config).setDelimiterParsingDisabled(true);
config.setProperty(CoreOptions.STORE.name(), graph);
config.setProperty(CoreOptions.BACKEND.name(), backend);
config.setProperty(CoreOptions.SERIALIZER.name(), serializer);
return ((HugeGraph) GraphFactory.open(config));
}
zhoney marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,23 @@

package com.baidu.hugegraph.testutil;

import java.io.File;
import java.util.Date;
import java.util.List;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Vertex;

import com.baidu.hugegraph.HugeException;
import com.baidu.hugegraph.HugeFactory;
import com.baidu.hugegraph.HugeGraph;
import com.baidu.hugegraph.backend.id.Id;
import com.baidu.hugegraph.testutil.FakeObjects.FakeEdge;
import com.baidu.hugegraph.testutil.FakeObjects.FakeVertex;
import com.baidu.hugegraph.util.DateUtil;
import com.baidu.hugegraph.util.E;

public class Utils {

Expand Down Expand Up @@ -80,4 +85,22 @@ public static boolean contains(List<Edge> edges, FakeEdge fakeEdge) {
public static Date date(String rawDate) {
return DateUtil.parse(rawDate);
}

public static PropertiesConfiguration getConf() {
String confFile = Utils.class.getClassLoader()
.getResource(CONF_PATH).getPath();
File file = new File(confFile);
E.checkArgument(file.exists() && file.isFile() && file.canRead(),
"Need to specify a readable config file rather than:" +
" %s", file.toString());

PropertiesConfiguration config;
try {
config = new PropertiesConfiguration(file);
} catch (ConfigurationException e) {
throw new HugeException("Unable to load config file: %s",
e, confFile);
}
return config;
}
}
Loading