Cassandra Persistor

The Cassandra persistor connects Quine to Cassandra and Cassandra compatible solutions like ScyllaDB, Astra DB, and Amazon Keyspaces.

Quine Configuration

To use Cassandra as the persistence backend for Quine, you’ll need to set the quine.store section to type = cassandra in the config.

quine.store {
  # store data in an Apache Cassandra instance
  type = cassandra

  # "host:port" strings at which Cassandra nodes can be accessed from
  # the application
  endpoints = [
    "localhost:9042"
  ]

  # the keyspace to use
  keyspace = quine

  # whether the application should create the keyspace if it does not
  # yet exist
  should-create-keyspace = true

  # whether the application should create tables in the keyspace if
  # they do not yet exist
  should-create-tables = true

  # how many copies of each datum the Cassandra cluster should retain
  replication-factor = 1

  # how many hosts must agree on a datum for Quine to consider that
  # datum written/read
  write-consistency = LOCAL_QUORUM
  read-consistency = LOCAL_QUORUM

  # Which Cassandra cluster datacenter is considered local to this deployment of Quine (e.g. us-west-2)
  local-datacenter = "datacenter1"

  # how long to wait before considering a write operation failed
  write-timeout = "10s"

  # how long to wait before considering a read operation failed
  read-timeout = "10s"

  # if set, the number of nodes for which to optimize node creation
  # latency
  # bloom-filter-size =
}

Where endpoints is a list of the address(es) of one or more Cassandra hosts in the cluster. If you need to specify a port other than 9042 (the default), you can use host:portNum.

Alternatively, you may specify the environment variable CASSANDRA_ENDPOINTS as a comma-separated list of hostnames, or host:ports, to be used if endpoints is not set in the config file.

Cassandra Authentication

Quine communicates with Cassandra via the DataStax Java Driver for Apache Cassandra. You can configure authentication by adding datastax-java-driver configuration to your local config file file as described on the driver’s Authentication page.

For example, adding the following into a quine.conf file will set up basic authentication.

quine.store {
  type = cassandra
}
datastax-java-driver {
  advanced {
    auth-provider {
      class = PlainTextAuthProvider
      username = user
      password = pass
    }
  }
}

Then launch Quine with the following command line:

java -Dconfig.file=quine.conf -jar quine.jar

Automatic Creation of Keyspace and Tables

Quine has settings in the Cassandra section of the config, should-create-keyspace and should-create-tables. When enabled, Quine automatically creates the keyspace and/or tables at startup if they don’t already exist.

Note

Auto creation of the keyspace and tables is included as a development convenience and should never be used in production.

Cassandra Schema

CREATE KEYSPACE quine WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'};
USE quine;

CREATE TABLE domain_graph_nodes (
    dgn_id bigint PRIMARY KEY,
    data blob
);

CREATE TABLE domain_index_events (
    quine_id blob,
    timestamp bigint,
    data blob,
    dgn_id bigint,
    PRIMARY KEY (quine_id, timestamp)
) WITH CLUSTERING ORDER BY (timestamp ASC)
    AND compaction = {'class': 'TimeWindowCompactionStrategy'};

CREATE TABLE journals (
    quine_id blob,
    timestamp bigint,
    data blob,
    PRIMARY KEY (quine_id, timestamp)
) WITH CLUSTERING ORDER BY (timestamp ASC)
    AND compaction = {'class': 'TimeWindowCompactionStrategy'};

CREATE TABLE meta_data (
    key text PRIMARY KEY,
    value blob
);

CREATE TABLE snapshots (
    quine_id blob,
    timestamp bigint,
    multipart_index int,
    data blob,
    multipart_count int,
    PRIMARY KEY (quine_id, timestamp, multipart_index)
) WITH CLUSTERING ORDER BY (timestamp DESC, multipart_index ASC);

CREATE TABLE standing_queries (
    query_id uuid PRIMARY KEY,
    queries blob
);

CREATE TABLE standing_query_states (
    quine_id blob,
    standing_query_id uuid,
    standing_query_part_id uuid,
    data blob,
    PRIMARY KEY (quine_id, standing_query_id, standing_query_part_id)
) WITH CLUSTERING ORDER BY (standing_query_id ASC, standing_query_part_id ASC);

AstraDB Configuration

Astra DB is a fully Cassandra compatible and serverless DbaaS that simplifies the development and deployment of high-growth applications.

It doesn’t support setting compaction strategies other than UnifiedCompactionStrategy, so you’ll need to remove the AND compaction = {'class': 'TimeWindowCompactionStrategy'} from the above schema. It also requires setting a token from Astra DB, as well as the path to a secure connect bundle .zip file from them.

An example config for this is as follows:

quine.store {
  # store data in Cassandra
  type = cassandra
  # the keyspace to use
  keyspace = quine
  should-create-keyspace = false
  replication-factor = 3
  local-datacenter = ${ASTRA_DB_REGION}
}
datastax-java-driver {
  advanced {
    auth-provider {
      class = PlainTextAuthProvider
      username = token
      password = "${ASTRA_DB_APP_TOKEN}"
    }
  }
  basic {
    cloud {
      secure-connect-bundle = "${SECURE-CONNECT-BUNDLE}.zip"
    }
  }
}

Astra DB Relevant Settings

type = cassandra - Use the Cassandra persistor to connect to AstraDB

should-create-keyspace = false - Keyspaces can only be created in Astra via the dashboard.

replication-factor = 3 - Defaults to 1 if not set.

write-consistency = LOCAL_QUORUM - Minimum consistency level required by Astra.

read-consistency = LOCAL_QUORUM - Any level is supported, though with replication-factor=3 and QUORUM writes, you’ll want reads to be QUORUM as well if you want them to be immediately reflect writes.

local-datacenter = "us-east1" - Set your Astra DB cloud region as the local DC.

username = "token" - Leave it as the literal word “token.”

password - A valid token for an Astra DB cluster.

secure-connect-bundle - A valid, local file location of a downloaded Astra secure connect bundle. The driver gets the Astra DB hostname from the secure bundle, so there is no need to specify endpoints separately.

Amazon Keyspaces Configuration

Keyspaces is a service provided by AWS that is mostly compatible with Cassandra. It has a higher latency for responding to queries, which translates into higher latencies for many Quine operations. Because it doesn’t support everything Cassanrda does, and its configuration is a bit different, we have a different persistor type for it: keypaces. At a minimum, setting quine.store.type = keyspaces should be all you need to use it. The rest of the config options are the same as that for Cassandra, with the following differences:

Removals

replication-factor - Fixed to 3, not modifiable.

write-consistency - Fixed to LOCAL_QUORUM, not modifiable.

local-datacenter - Set to the same value as the AWS region.

Additions

aws-region - The AWS region to connect to Keyspaces in (e.g. “us-west-2”. If unspecified, the region sourced from one of the mechanisms on the AWS SDK’s DefaultAwsRegionProviderChain.

aws-role-arn - The ARN of an IAM role to assume. It must have read / write access to the desired keyspace, as well as select access to the system keyspaces system, system_schema, and system_schema_mcs.

AWS credentials are required, which should provided in the environment using one of the mechanisms on https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/credentials-chain.html

The full default config for Keyspaces is as follows:

quine.store {
  # store data in Cassandra
  type = keyspaces

  # the keyspace to use
  keyspace = quine

  # If unspecified, sourced from one of the mechanisms on https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/regions/providers/DefaultAwsRegionProviderChain.html.
  # aws-region =

  # The ARN of an IAM role to assume. It must have read / write access to the desired keyspace, as well as `select` access to the system keyspaces `system`, `system_schema`, and `system_schema_mcs`.
  # aws-role-arn =

  # whether the application should create the keyspace if it does not
  # yet exist
  should-create-keyspace = true

  # whether the application should create tables in the keyspace if
  # they do not yet exist
  should-create-tables = true

  read-consistency = LOCAL_QUORUM

  # how long to wait before considering a write operation failed
  write-timeout = "10s"

  # how long to wait before considering a read operation failed
  read-timeout = "10s"

  # if set, the number of nodes for which to optimize node creation
  # latency
  # bloom-filter-size =
}