From 6aeb0eafe63ec4c233c896fd7a94e2f7dfe2e815 Mon Sep 17 00:00:00 2001 From: Abhishek Singh Date: Sun, 3 Mar 2024 16:07:36 +0530 Subject: [PATCH] cassandra: init service (#116) * cassandra: init service * cassandra: add cassandra/conf files * cassandra_test: change package to cassandra_4 * update readiness_probe * update cassandra_test * cassandra: add doc * cassandra_4 doesn't require python-2 --------- Co-authored-by: shivaraj-bh --- doc/cassandra.md | 31 ++++++++ doc/services.md | 1 + nix/cassandra.nix | 169 +++++++++++++++++++++++++++++++++++++++++ nix/cassandra_test.nix | 27 +++++++ nix/default.nix | 1 + test/flake.nix | 1 + 6 files changed, 230 insertions(+) create mode 100644 doc/cassandra.md create mode 100644 nix/cassandra.nix create mode 100644 nix/cassandra_test.nix diff --git a/doc/cassandra.md b/doc/cassandra.md new file mode 100644 index 0000000..c60f53a --- /dev/null +++ b/doc/cassandra.md @@ -0,0 +1,31 @@ +# Cassandra + +[Cassandra] is a free and open-source, distributed, wide-column store, NoSQL database management system designed to handle large amounts of data across many commodity servers, providing high availability with no single point of failure. + +[Cassandra]: https://cassandra.apache.org/_/index.html + +## Getting Started + +```nix +# In `perSystem.process-compose.` +{ + services.cassandra."cass1".enable = true; +} +``` + +{#tips} +## Tips & Tricks + +{#change-port} +### Change the default port + +By default, the Cassandra server is started on port `9042`. To change the port, we can use the following config: + +```nix +{ + services.cassandra."cass1" = { + enable = true; + nativeTransportPort = 9043; + }; +} +``` diff --git a/doc/services.md b/doc/services.md index 3334f21..6abc7bc 100644 --- a/doc/services.md +++ b/doc/services.md @@ -19,6 +19,7 @@ short-title: Services - [x] [[grafana]]# - [X] [[prometheus]]# - [X] [[pgadmin]]# +- [X] [[cassandra]]# - [ ] ... [gh]: https://github.com/juspay/services-flake diff --git a/nix/cassandra.nix b/nix/cassandra.nix new file mode 100644 index 0000000..a28aeea --- /dev/null +++ b/nix/cassandra.nix @@ -0,0 +1,169 @@ +# Based on https://github.com/cachix/devenv/blob/fa9a708e240c6174f9fc4c6eefbc6a89ce01c350/src/modules/services/cassandra.nix +{ pkgs, lib, name, config, ... }: +let + inherit (lib) types; + yamlFormat = pkgs.formats.yaml { }; +in +{ + options = { + enable = lib.mkEnableOption name; + + package = lib.mkPackageOption pkgs "cassandra" { }; + + dataDir = lib.mkOption { + type = types.str; + default = "./data/${name}"; + description = "The cassandra data directory"; + }; + + listenAddress = lib.mkOption { + type = types.str; + description = "Listen address"; + default = "127.0.0.1"; + example = "127.0.0.1"; + }; + + nativeTransportPort = lib.mkOption { + type = types.port; + description = "port for the CQL native transport to listen for clients on"; + default = 9042; + }; + + seedAddresses = lib.mkOption { + type = types.listOf types.str; + default = [ "127.0.0.1" ]; + description = "The addresses of hosts designated as contact points of the cluster"; + }; + + clusterName = lib.mkOption { + type = types.str; + default = "Test Cluster"; + description = "The name of the cluster"; + }; + + allowClients = lib.mkOption { + type = types.bool; + default = true; + description = '' + Enables or disables the native transport server (CQL binary protocol) + ''; + }; + + extraConfig = lib.mkOption { + type = yamlFormat.type; + default = { }; + example = + { + commitlog_sync_batch_window_in_ms = 3; + }; + description = '' + Extra options to be merged into `cassandra.yaml` as nix attribute set. + ''; + }; + + jvmOpts = lib.mkOption { + type = types.listOf types.str; + default = [ ]; + description = "Options to pass to the JVM through the JVM_OPTS environment variable"; + }; + + defaultExtraConfig = lib.mkOption { + type = yamlFormat.type; + internal = true; + readOnly = true; + default = { + start_native_transport = config.allowClients; + listen_address = config.listenAddress; + native_transport_port = config.nativeTransportPort; + commitlog_sync = "batch"; + commitlog_sync_batch_window_in_ms = 2; + cluster_name = config.clusterName; + partitioner = "org.apache.cassandra.dht.Murmur3Partitioner"; + endpoint_snitch = "SimpleSnitch"; + data_file_directories = [ "${config.dataDir}/data" ]; + commitlog_directory = "${config.dataDir}/commitlog"; + saved_caches_directory = "${config.dataDir}/saved_caches"; + hints_directory = "${config.dataDir}/hints"; + seed_provider = [ + { + class_name = "org.apache.cassandra.locator.SimpleSeedProvider"; + parameters = [{ seeds = lib.concatStringsSep "," config.seedAddresses; }]; + } + ]; + }; + }; + + outputs.settings = lib.mkOption { + type = types.deferredModule; + internal = true; + readOnly = true; + default = { + processes = { + "${name}" = + let + cassandraConfig = pkgs.stdenv.mkDerivation { + name = "cassandra-config"; + cassandraYaml = yamlFormat.generate "cassandra.yaml" ( + lib.recursiveUpdate config.defaultExtraConfig config.extraConfig + ); + buildCommand = '' + mkdir -p $out + for d in ${config.package}/conf/*; do ln -s "$d" $out/; done + rm -rf $out/cassandra.y*ml + ln -s "$cassandraYaml" "$out/cassandra.yaml" + + rm -rf $out/cassandra-env.sh + cat ${config.package}/conf/cassandra-env.sh > $out/cassandra-env.sh + LOCAL_JVM_OPTS="${lib.concatStringsSep " " config.jvmOpts}" + echo "JVM_OPTS=\"\$JVM_OPTS $LOCAL_JVM_OPTS\"" >> $out/cassandra-env.sh + ''; + }; + + startScript = pkgs.writeShellScriptBin "start-cassandra" '' + set -euo pipefail + + DATA_DIR="$(readlink -m ${config.dataDir})" + if [[ ! -d "$DATA_DIR" ]]; then + mkdir -p "$DATA_DIR" + fi + + CASSANDRA_CONF="${cassandraConfig}" + export CASSANDRA_CONF + + CASSANDRA_LOG_DIR="$DATA_DIR/log/" + mkdir -p "$CASSANDRA_LOG_DIR" + export CASSANDRA_LOG_DIR + + CASSANDRA_HOME="${config.package}" + export CASSANDRA_HOME + + CLASSPATH="${config.package}/lib" + export CLASSPATH + + export LOCAL_JMX="yes" + exec ${config.package}/bin/cassandra -f + ''; + in + { + command = startScript; + + readiness_probe = { + exec.command = '' + echo 'show version;' | CQLSH_HOST=${config.listenAddress} CQLSH_PORT=${toString config.nativeTransportPort} ${config.package}/bin/cqlsh + ''; + initial_delay_seconds = 2; + period_seconds = 10; + timeout_seconds = 4; + success_threshold = 1; + failure_threshold = 5; + }; + namespace = name; + + # https://github.com/F1bonacc1/process-compose#-auto-restart-if-not-healthy + availability.restart = "on_failure"; + }; + }; + }; + }; + }; +} diff --git a/nix/cassandra_test.nix b/nix/cassandra_test.nix new file mode 100644 index 0000000..07ff559 --- /dev/null +++ b/nix/cassandra_test.nix @@ -0,0 +1,27 @@ +{ pkgs, config, ... }: { + services.cassandra."cass1" = { + enable = true; + # support for aarch64-darwin was added in cassandra-4 + # https://github.com/apache/cassandra/blob/a87055d56a33a9b17606f14535f48eb461965b82/CHANGES.txt#L192 + package = pkgs.cassandra_4; + }; + + settings.processes.test = + let + cfg = config.services.cassandra."cass1"; + in + { + command = pkgs.writeShellApplication { + runtimeInputs = [ cfg.package ]; + text = '' + echo "show version;" | cqlsh + echo "create keyspace test with replication = {'class': 'SimpleStrategy', 'replication_factor': 1};" | cqlsh + echo "CREATE TABLE test.test_table(id int PRIMARY KEY, name text);" | cqlsh + echo "insert into test.test_table (id, name) VALUES (1, 'hello');" | cqlsh + echo "select * from test.test_table;" | cqlsh | grep hello + ''; + name = "cassandra-test"; + }; + depends_on."cass1".condition = "process_healthy"; + }; +} diff --git a/nix/default.nix b/nix/default.nix index 0ae9f0a..f3350f8 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -16,5 +16,6 @@ in ./grafana.nix ./prometheus.nix ./pgadmin.nix + ./cassandra.nix ]; } diff --git a/test/flake.nix b/test/flake.nix index 182ec76..c94d121 100644 --- a/test/flake.nix +++ b/test/flake.nix @@ -55,6 +55,7 @@ "${inputs.services-flake}/nix/grafana_test.nix" "${inputs.services-flake}/nix/prometheus_test.nix" "${inputs.services-flake}/nix/pgadmin_test.nix" + "${inputs.services-flake}/nix/cassandra_test.nix" ]); }; };