Performance issues when working from a remote client


#1

Hello all,

When I’m connecting to a remote standalone Grakn server, relatively simple operations, take much longer time than expected.

I haven’t touched any of the configurations (i.e. Titan & Cassandra backend), except changing IP addresses according to this discussion.

I wrote a minimal ontology:

OntologyEntity sub entity, has ontology-name;
ontology-name sub resource, datatype string;
$e isa OntologyEntity, has ontology-name “name”;

And my Java app does the following steps:

  1. Receives GraknGraph (WRITE) connection.
  2. Deletes the entire keyspace.
  3. Receives another GraknGraph (WRITE) connection.
  4. Loads the ontology through a GraqlAPI and commits it.
  5. Receives GraknGraph (READ) connection.
  6. Queries the ontology-name of my OntologyEntity with the GraqlAPI:

match $e isa OntologyEntity, has ontology-name $n;

I’m running the same .jar file from three different machines:

  • a) The machine that runs the server.
  • b) Remote client in the same country as the first one.
  • c) My machine, in a different country.

Receiving the first WRITE connection on my machine (1.c) takes ~20 sec.

The second WRITE connection (3.c), that I assume also creates the keyspace from scratch - takes up to a minute.

Running the READ query (6.c) takes ~3 sec.

Running it on machine b is about 5 times faster, but is still quite slow, if I would like to write data in real-time…

Is this normal behavior?

Are there any “quick” changes I can apply in order to improve the performance, or do I need to implement a solution similar to what you did with the API.AI chatbot (i.e. messaging the queries/parameters to the server) in order to receive quick responses?

Thanks,
Ilya.


#2

Can you show us your java code?


#3
package ilyash;

import static ai.grakn.graql.internal.pattern.Patterns.var;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;

import javax.annotation.Nonnull;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import ai.grakn.Grakn;
import ai.grakn.GraknGraph;
import ai.grakn.GraknSession;
import ai.grakn.GraknTxType;
import ai.grakn.exception.GraqlQueryException;

@SpringBootApplication
public class GrakntesterApplication implements ApplicationRunner {

	private static final Logger logger = LoggerFactory.getLogger(GrakntesterApplication.class);

	private GraknSession session;
	private GraknGraph graph;

	private Instant start;
	private Instant lastLogInstant;

	public static void main(String[] args) {
		SpringApplication.run(GrakntesterApplication.class, args);
	}

	@Override
	public void run(ApplicationArguments args) throws Exception {
		logger.info("Application started with command-line arguments: {}", Arrays.toString(args.getSourceArgs()));

		if (args.getOptionNames().containsAll(Arrays.asList("a", "k", "f"))){
			String address = args.getOptionValues("a").iterator().next();
			String keyspace = args.getOptionValues("k").iterator().next();
			String filepath = args.getOptionValues("f").iterator().next();
			testGraknPerformance(address, keyspace, filepath, true, false, false);
		}
		else {
			StringBuilder sb = new StringBuilder("Usage: program starts with the following mandatory arguments:\n");
			sb.append("--a=grakn.server.ipv4.address\n");
			sb.append("--k=keyspace_name\n");
			sb.append("--f=filepath.gql");
			logger.error(sb.toString());
		}

		logger.info("Goodbye!");
	}

	public void testGraknPerformance(String address, @Nonnull String keyspace, String filepath, boolean infer, boolean readOnly, boolean inMemory) {
		start = lastLogInstant = Instant.now();
		if (inMemory == true) {
			address = Grakn.IN_MEMORY;
		}
		session = Grakn.session(address, keyspace);
		if (readOnly == false) {
			graph = connectToServer(session, GraknTxType.WRITE);
			clearGraph(graph);
			graph = connectToServer(session, GraknTxType.WRITE);
			loadOntologyFromGqlFile(graph, filepath);
		}
		
		graph = connectToServer(session, GraknTxType.READ);
		testConnection(graph);
		
		graph.close();
		session.close();
	}

	private void testConnection(@Nonnull GraknGraph graph) {
		getOntologyNameOfOntologyEntityType(graph, "OntologyEntity");
	}

	private String getOntologyNameOfOntologyEntityType(@Nonnull GraknGraph graph, @Nonnull String sOntologyEntityType) {
		if (sOntologyEntityType == null) {
			logger.warn("Given OntologyEntityType is null.");
			return sOntologyEntityType;
		}
		String name = graph.graql().match(
				var("e").isa(sOntologyEntityType).has("ontology-name", var("name"))
		).get("name").iterator().next().asResource().getValue().toString();
		timer(String.format("%s's ontology-name is: %s", sOntologyEntityType, name));
		return name;
	}

	private void clearGraph(@Nonnull GraknGraph graph) {
		String keyspace = graph.getKeyspace();
		graph.admin().delete();
		timer("Cleared graph and deleted keyspace: " + keyspace);
	}

	private void loadOntologyFromGqlFile(@Nonnull GraknGraph graph, @Nonnull String filepath) {
		String gqlContent = null;
		try {
			gqlContent = new String(Files.readAllBytes(Paths.get(filepath)));
		} catch (IOException e) {
			logger.warn("Failed to open .gql file: "+filepath);
			return;
		}
		try {
			graph.graql().parse(gqlContent).execute();
			graph.commit();
		} catch (GraqlQueryException e) {
			logger.warn(e.getLocalizedMessage());
			logger.warn("Failed to load data from .gql file: "+filepath);
			return;
		}
		timer("Loaded data from .gql file: " + filepath);
	}

	private GraknGraph connectToServer(@Nonnull GraknSession session, @Nonnull GraknTxType graknTxType){
		graph = session.open(graknTxType);
		timer(String.format("Connected to Grakn keyspace (%s): %s", graknTxType, graph.getKeyspace()));
		return graph;
	}

	private void timer(@Nonnull String s) {
		logger.info(s);
		logger.info(String.format("TIMER: %s from last timer-log (%s total).\n",
				Duration.between(lastLogInstant, Instant.now()), Duration.between(start, Instant.now())));
		lastLogInstant = Instant.now();
	}
}