···
cfg = config.services.cassandra;
7
-
cassandraPackage = cfg.package.override {
12
-
home = "/var/lib/cassandra";
13
-
description = "Cassandra role user";
7
+
defaultUser = "cassandra";
8
+
cassandraConfig = flip recursiveUpdate cfg.extraConfig
9
+
({ commitlog_sync = "batch";
10
+
commitlog_sync_batch_window_in_ms = 2;
11
+
partitioner = "org.apache.cassandra.dht.Murmur3Partitioner";
12
+
endpoint_snitch = "SimpleSnitch";
14
+
[{ class_name = "org.apache.cassandra.locator.SimpleSeedProvider";
15
+
parameters = [ { seeds = "127.0.0.1"; } ];
17
+
data_file_directories = [ "${cfg.homeDir}/data" ];
18
+
commitlog_directory = "${cfg.homeDir}/commitlog";
19
+
saved_caches_directory = "${cfg.homeDir}/saved_caches";
20
+
} // (if builtins.compareVersions cfg.package.version "3" >= 0
21
+
then { hints_directory = "${cfg.homeDir}/hints"; }
24
+
cassandraConfigWithAddresses = cassandraConfig //
25
+
( if isNull cfg.listenAddress
26
+
then { listen_interface = cfg.listenInterface; }
27
+
else { listen_address = cfg.listenAddress; }
29
+
if isNull cfg.rpcAddress
30
+
then { rpc_interface = cfg.rpcInterface; }
31
+
else { rpc_address = cfg.rpcAddress; }
33
+
cassandraEtc = pkgs.stdenv.mkDerivation
34
+
{ name = "cassandra-etc";
35
+
cassandraYaml = builtins.toJSON cassandraConfigWithAddresses;
36
+
cassandraEnvPkg = "${cfg.package}/conf/cassandra-env.sh";
16
-
cassandraRackDcProperties = ''
22
-
cluster_name: ${cfg.clusterName}
24
-
auto_bootstrap: ${boolToString cfg.autoBootstrap}
25
-
hinted_handoff_enabled: ${boolToString cfg.hintedHandOff}
26
-
hinted_handoff_throttle_in_kb: ${builtins.toString cfg.hintedHandOffThrottle}
27
-
max_hints_delivery_threads: 2
28
-
max_hint_window_in_ms: 10800000 # 3 hours
29
-
authenticator: ${cfg.authenticator}
30
-
authorizer: ${cfg.authorizer}
31
-
permissions_validity_in_ms: 2000
32
-
partitioner: org.apache.cassandra.dht.Murmur3Partitioner
33
-
data_file_directories:
34
-
${builtins.concatStringsSep "\n" (map (v: " - "+v) cfg.dataDirs)}
35
-
commitlog_directory: ${cfg.commitLogDirectory}
36
-
disk_failure_policy: stop
37
-
key_cache_size_in_mb:
38
-
key_cache_save_period: 14400
39
-
row_cache_size_in_mb: 0
40
-
row_cache_save_period: 0
41
-
saved_caches_directory: ${cfg.savedCachesDirectory}
42
-
commitlog_sync: ${cfg.commitLogSync}
43
-
commitlog_sync_period_in_ms: ${builtins.toString cfg.commitLogSyncPeriod}
44
-
commitlog_segment_size_in_mb: 32
46
-
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
48
-
- seeds: "${builtins.concatStringsSep "," cfg.seeds}"
49
-
concurrent_reads: ${builtins.toString cfg.concurrentReads}
50
-
concurrent_writes: ${builtins.toString cfg.concurrentWrites}
51
-
memtable_flush_queue_size: 4
52
-
trickle_fsync: false
53
-
trickle_fsync_interval_in_kb: 10240
55
-
ssl_storage_port: 7001
56
-
listen_address: ${cfg.listenAddress}
57
-
start_native_transport: true
58
-
native_transport_port: 9042
60
-
rpc_address: ${cfg.rpcAddress}
63
-
rpc_server_type: sync
64
-
thrift_framed_transport_size_in_mb: 15
65
-
incremental_backups: ${boolToString cfg.incrementalBackups}
66
-
snapshot_before_compaction: false
68
-
column_index_size_in_kb: 64
69
-
in_memory_compaction_limit_in_mb: 64
70
-
multithreaded_compaction: false
71
-
compaction_throughput_mb_per_sec: 16
72
-
compaction_preheat_key_cache: true
73
-
read_request_timeout_in_ms: 10000
74
-
range_request_timeout_in_ms: 10000
75
-
write_request_timeout_in_ms: 10000
76
-
cas_contention_timeout_in_ms: 1000
77
-
truncate_request_timeout_in_ms: 60000
78
-
request_timeout_in_ms: 10000
79
-
cross_node_timeout: false
80
-
endpoint_snitch: ${cfg.snitch}
81
-
dynamic_snitch_update_interval_in_ms: 100
82
-
dynamic_snitch_reset_interval_in_ms: 600000
83
-
dynamic_snitch_badness_threshold: 0.1
84
-
request_scheduler: org.apache.cassandra.scheduler.NoScheduler
85
-
server_encryption_options:
86
-
internode_encryption: ${cfg.internodeEncryption}
87
-
keystore: ${cfg.keyStorePath}
88
-
keystore_password: ${cfg.keyStorePassword}
89
-
truststore: ${cfg.trustStorePath}
90
-
truststore_password: ${cfg.trustStorePassword}
91
-
client_encryption_options:
92
-
enabled: ${boolToString cfg.clientEncryption}
93
-
keystore: ${cfg.keyStorePath}
94
-
keystore_password: ${cfg.keyStorePassword}
95
-
internode_compression: all
96
-
inter_dc_tcp_nodelay: false
97
-
preheat_kernel_page_cache: false
98
-
streaming_socket_timeout_in_ms: ${toString cfg.streamingSocketTimoutInMS}
102
-
log4j.rootLogger=${cfg.logLevel},stdout
103
-
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
104
-
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
105
-
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] %d{HH:mm:ss,SSS} %m%n
108
-
cassandraConfFile = pkgs.writeText "cassandra.yaml" cassandraConf;
109
-
cassandraLogFile = pkgs.writeText "log4j-server.properties" cassandraLog;
110
-
cassandraRackFile = pkgs.writeText "cassandra-rackdc.properties" cassandraRackDcProperties;
112
-
cassandraEnvironment = {
113
-
CASSANDRA_HOME = cassandraPackage;
114
-
JAVA_HOME = cfg.jre;
115
-
CASSANDRA_CONF = "/etc/cassandra";
40
+
echo "$cassandraYaml" > "$out/cassandra.yaml"
41
+
ln -s "$cassandraEnvPkg" "$out/cassandra-env.sh"
options.services.cassandra = {
123
-
enable = mkOption {
124
-
description = "Whether to enable cassandra.";
128
-
package = mkOption {
129
-
description = "Cassandra package to use.";
130
-
default = pkgs.cassandra;
131
-
defaultText = "pkgs.cassandra";
132
-
type = types.package;
135
-
description = "JRE package to run cassandra service.";
136
-
default = pkgs.jre;
137
-
defaultText = "pkgs.jre";
138
-
type = types.package;
46
+
enable = mkEnableOption ''
47
+
Apache Cassandra – Scalable and highly available database.
141
-
description = "User that runs cassandra service.";
142
-
default = "cassandra";
143
-
type = types.string;
51
+
default = defaultUser;
52
+
description = "Run Apache Cassandra under this user.";
146
-
description = "Group that runs cassandra service.";
147
-
default = "cassandra";
148
-
type = types.string;
150
-
envFile = mkOption {
151
-
description = "path to cassandra-env.sh";
152
-
default = "${cassandraPackage}/conf/cassandra-env.sh";
153
-
defaultText = "\${cassandraPackage}/conf/cassandra-env.sh";
156
-
clusterName = mkOption {
157
-
description = "set cluster name";
158
-
default = "cassandra";
159
-
example = "prod-cluster0";
160
-
type = types.string;
162
-
commitLogDirectory = mkOption {
163
-
description = "directory for commit logs";
164
-
default = "/var/lib/cassandra/commit_log";
165
-
type = types.string;
167
-
savedCachesDirectory = mkOption {
168
-
description = "directory for saved caches";
169
-
default = "/var/lib/cassandra/saved_caches";
170
-
type = types.string;
172
-
hintedHandOff = mkOption {
173
-
description = "enable hinted handoff";
177
-
hintedHandOffThrottle = mkOption {
178
-
description = "hinted hand off throttle rate in kb";
182
-
commitLogSync = mkOption {
183
-
description = "commitlog sync method";
184
-
default = "periodic";
188
-
commitLogSyncPeriod = mkOption {
189
-
description = "commitlog sync period in ms ";
56
+
default = defaultUser;
57
+
description = "Run Apache Cassandra under this group.";
193
-
envScript = mkOption {
194
-
default = "${cassandraPackage}/conf/cassandra-env.sh";
195
-
defaultText = "\${cassandraPackage}/conf/cassandra-env.sh";
59
+
homeDir = mkOption {
197
-
description = "Supply your own cassandra-env.sh rather than using the default";
61
+
default = "/var/lib/cassandra";
63
+
Home directory for Apache Cassandra.
199
-
extraParams = mkOption {
200
-
description = "add additional lines to cassandra-env.sh";
202
-
example = [''JVM_OPTS="$JVM_OPTS -Dcassandra.available_processors=1"''];
203
-
type = types.listOf types.str;
66
+
package = mkOption {
67
+
type = types.package;
68
+
default = pkgs.cassandra;
69
+
defaultText = "pkgs.cassandra";
70
+
example = literalExample "pkgs.cassandra_3_11";
72
+
The Apache Cassandra package to use.
205
-
dataDirs = mkOption {
206
-
type = types.listOf types.path;
207
-
default = [ "/var/lib/cassandra/data" ];
208
-
description = "Data directories for cassandra";
210
-
logLevel = mkOption {
213
-
description = "default logging level for log4j";
215
-
internodeEncryption = mkOption {
216
-
description = "enable internode encryption";
221
-
clientEncryption = mkOption {
222
-
description = "enable client encryption";
226
-
trustStorePath = mkOption {
227
-
description = "path to truststore";
228
-
default = ".conf/truststore";
231
-
keyStorePath = mkOption {
232
-
description = "path to keystore";
233
-
default = ".conf/keystore";
236
-
keyStorePassword = mkOption {
237
-
description = "password to keystore";
238
-
default = "cassandra";
241
-
trustStorePassword = mkOption {
242
-
description = "password to truststore";
243
-
default = "cassandra";
247
-
description = "password to truststore";
248
-
default = [ "127.0.0.1" ];
75
+
jvmOpts = mkOption {
type = types.listOf types.str;
251
-
concurrentWrites = mkOption {
252
-
description = "number of concurrent writes allowed";
256
-
concurrentReads = mkOption {
257
-
description = "number of concurrent reads allowed";
79
+
Populate the JVM_OPT environment variable.
listenAddress = mkOption {
262
-
description = "listen address";
263
-
default = "localhost";
83
+
type = types.nullOr types.str;
84
+
default = "127.0.0.1";
85
+
example = literalExample "null";
87
+
Address or interface to bind to and tell other Cassandra nodes
88
+
to connect to. You _must_ change this if you want multiple
89
+
nodes to be able to communicate!
91
+
Set listenAddress OR listenInterface, not both.
93
+
Leaving it blank leaves it up to
94
+
InetAddress.getLocalHost(). This will always do the Right
95
+
Thing _if_ the node is properly configured (hostname, name
96
+
resolution, etc), and the Right Thing is to use the address
97
+
associated with the hostname (it might not be).
99
+
Setting listen_address to 0.0.0.0 is always wrong.
266
-
rpcAddress = mkOption {
267
-
description = "rpc listener address";
268
-
default = "localhost";
271
-
incrementalBackups = mkOption {
272
-
description = "enable incremental backups";
276
-
snitch = mkOption {
277
-
description = "snitch to use for topology discovery";
278
-
default = "GossipingPropertyFileSnitch";
279
-
example = "Ec2Snitch";
283
-
description = "datacenter for use in topology configuration";
289
-
description = "rack for use in topology configuration";
294
-
authorizer = mkOption {
296
-
Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
298
-
default = "AllowAllAuthorizer";
299
-
example = "CassandraAuthorizer";
302
-
authenticator = mkOption {
304
-
Authentication backend, implementing IAuthenticator; used to identify users
306
-
default = "AllowAllAuthenticator";
307
-
example = "PasswordAuthenticator";
310
-
autoBootstrap = mkOption {
311
-
description = "It makes new (non-seed) nodes automatically migrate the right data to themselves.";
315
-
streamingSocketTimoutInMS = mkOption {
316
-
description = "Enable or disable socket timeout for streaming operations";
317
-
default = 3600000; #CASSANDRA-8611
321
-
repairStartAt = mkOption {
323
-
type = types.string;
102
+
listenInterface = mkOption {
103
+
type = types.nullOr types.str;
325
-
Defines realtime (i.e. wallclock) timers with calendar event
326
-
expressions. For more details re: systemd OnCalendar at
327
-
https://www.freedesktop.org/software/systemd/man/systemd.time.html#Displaying%20Time%20Spans
107
+
Set listenAddress OR listenInterface, not both. Interfaces
108
+
must correspond to a single address, IP aliasing is not
329
-
example = ["weekly" "daily" "08:05:40" "mon,fri *-1/2-1,3 *:30:45"];
331
-
repairRandomizedDelayInSec = mkOption {
334
-
description = ''Delay the timer by a randomly selected, evenly distributed
335
-
amount of time between 0 and the specified time value. re: systemd timer
336
-
RandomizedDelaySec for more details
112
+
rpcAddress = mkOption {
113
+
type = types.nullOr types.str;
114
+
default = "127.0.0.1";
115
+
example = literalExample "null";
117
+
The address or interface to bind the native transport server to.
119
+
Set rpcAddress OR rpcInterface, not both.
121
+
Leaving rpcAddress blank has the same effect as on
122
+
listenAddress (i.e. it will be based on the configured hostname
125
+
Note that unlike listenAddress, you can specify 0.0.0.0, but you
126
+
must also set extraConfig.broadcast_rpc_address to a value other
129
+
For security reasons, you should not expose this port to the
130
+
internet. Firewall it if needed.
339
-
repairPostStop = mkOption {
133
+
rpcInterface = mkOption {
134
+
type = types.nullOr types.str;
341
-
type = types.nullOr types.string;
343
-
Run a script when repair is over. One can use it to send statsd events, email, etc.
138
+
Set rpcAddress OR rpcInterface, not both. Interfaces must
139
+
correspond to a single address, IP aliasing is not supported.
346
-
repairPostStart = mkOption {
348
-
type = types.nullOr types.string;
143
+
extraConfig = mkOption {
144
+
type = types.attrs;
147
+
{ commitlog_sync_batch_window_in_ms = 3;
350
-
Run a script when repair starts. One can use it to send statsd events, email, etc.
351
-
It has same semantics as systemd ExecStopPost; So, if it fails, unit is consisdered
150
+
Extra options to be merged into cassandra.yaml as nix attribute set.
357
-
###### implementation
359
-
config = mkIf cfg.enable {
153
+
fullRepairInterval = mkOption {
154
+
type = types.nullOr types.str;
156
+
example = literalExample "null";
158
+
Set the interval how often full repairs are run, i.e.
159
+
`nodetool repair --full` is executed. See
160
+
https://cassandra.apache.org/doc/latest/operating/repair.html
161
+
for more information.
361
-
environment.etc."cassandra/cassandra-rackdc.properties" = {
362
-
source = cassandraRackFile;
163
+
Set to `null` to disable full repairs.
364
-
environment.etc."cassandra/cassandra.yaml" = {
365
-
source = cassandraConfFile;
166
+
fullRepairOptions = mkOption {
167
+
type = types.listOf types.str;
169
+
example = [ "--partitioner-range" ];
171
+
Options passed through to the full repair command.
367
-
environment.etc."cassandra/log4j-server.properties" = {
368
-
source = cassandraLogFile;
174
+
incrementalRepairInterval = mkOption {
175
+
type = types.nullOr types.str;
177
+
example = literalExample "null";
179
+
Set the interval how often incremental repairs are run, i.e.
180
+
`nodetool repair` is executed. See
181
+
https://cassandra.apache.org/doc/latest/operating/repair.html
182
+
for more information.
184
+
Set to `null` to disable incremental repairs.
370
-
environment.etc."cassandra/cassandra-env.sh" = {
372
-
${builtins.readFile cfg.envFile}
373
-
${concatStringsSep "\n" cfg.extraParams}
187
+
incrementalRepairOptions = mkOption {
188
+
type = types.listOf types.string;
190
+
example = [ "--partitioner-range" ];
192
+
Options passed through to the incremental repair command.
376
-
systemd.services.cassandra = {
377
-
description = "Cassandra Daemon";
378
-
wantedBy = [ "multi-user.target" ];
379
-
after = [ "network.target" ];
380
-
environment = cassandraEnvironment;
381
-
restartTriggers = [ cassandraConfFile cassandraLogFile cassandraRackFile ];
385
-
PermissionsStartOnly = true;
386
-
LimitAS = "infinity";
387
-
LimitNOFILE = "100000";
388
-
LimitNPROC = "32768";
389
-
LimitMEMLOCK = "infinity";
393
-
${cassandraPackage}/bin/cassandra -f
197
+
config = mkIf cfg.enable {
200
+
((isNull cfg.listenAddress)
201
+
|| (isNull cfg.listenInterface)
202
+
) && !((isNull cfg.listenAddress)
203
+
&& (isNull cfg.listenInterface)
205
+
message = "You have to set either listenAddress or listenInterface";
208
+
((isNull cfg.rpcAddress)
209
+
|| (isNull cfg.rpcInterface)
210
+
) && !((isNull cfg.rpcAddress)
211
+
&& (isNull cfg.rpcInterface)
213
+
message = "You have to set either rpcAddress or rpcInterface";
401
-
mkdir -m 0700 -p /etc/cassandra/triggers
402
-
mkdir -m 0700 -p /var/lib/cassandra /var/log/cassandra
403
-
chown ${cfg.user} /var/lib/cassandra /var/log/cassandra /etc/cassandra/triggers
407
-
while ! nodetool status >/dev/null 2>&1; do
216
+
users = mkIf (cfg.user == defaultUser) {
217
+
extraUsers."${defaultUser}" =
218
+
{ group = cfg.group;
219
+
home = cfg.homeDir;
221
+
uid = config.ids.uids.cassandra;
222
+
description = "Cassandra service user";
224
+
extraGroups."${defaultUser}".gid = config.ids.gids.cassandra;
414
-
environment.systemPackages = [ cassandraPackage ];
416
-
networking.firewall.allowedTCPPorts = [
423
-
users.users.cassandra =
424
-
if config.ids.uids ? "cassandra"
425
-
then { uid = config.ids.uids.cassandra; } // cassandraUser
426
-
else cassandraUser ;
428
-
boot.kernel.sysctl."vm.swappiness" = pkgs.lib.mkOptionDefault 0;
227
+
systemd.services.cassandra =
228
+
{ description = "Apache Cassandra service";
229
+
after = [ "network.target" ];
231
+
{ CASSANDRA_CONF = "${cassandraEtc}";
232
+
JVM_OPTS = builtins.concatStringsSep " " cfg.jvmOpts;
234
+
wantedBy = [ "multi-user.target" ];
238
+
ExecStart = "${cfg.package}/bin/cassandra -f";
239
+
SuccessExitStatus = 143;
430
-
systemd.timers."cassandra-repair" = {
432
-
OnCalendar = "${toString cfg.repairStartAt}";
433
-
RandomizedDelaySec = cfg.repairRandomizedDelayInSec;
243
+
systemd.services.cassandra-full-repair =
244
+
{ description = "Perform a full repair on this Cassandra node";
245
+
after = [ "cassandra.service" ];
246
+
requires = [ "cassandra.service" ];
251
+
lib.concatStringsSep " "
252
+
([ "${cfg.package}/bin/nodetool" "repair" "--full"
253
+
] ++ cfg.fullRepairOptions);
256
+
systemd.timers.cassandra-full-repair =
257
+
mkIf (!isNull cfg.fullRepairInterval) {
258
+
description = "Schedule full repairs on Cassandra";
259
+
wantedBy = [ "timers.target" ];
261
+
{ OnBootSec = cfg.fullRepairInterval;
262
+
OnUnitActiveSec = cfg.fullRepairInterval;
437
-
systemd.services."cassandra-repair" = {
438
-
description = "Cassandra repair daemon";
439
-
environment = cassandraEnvironment;
440
-
script = "${cassandraPackage}/bin/nodetool repair -pr";
441
-
postStop = mkIf (cfg.repairPostStop != null) cfg.repairPostStop;
442
-
postStart = mkIf (cfg.repairPostStart != null) cfg.repairPostStart;
267
+
systemd.services.cassandra-incremental-repair =
268
+
{ description = "Perform an incremental repair on this cassandra node.";
269
+
after = [ "cassandra.service" ];
270
+
requires = [ "cassandra.service" ];
275
+
lib.concatStringsSep " "
276
+
([ "${cfg.package}/bin/nodetool" "repair"
277
+
] ++ cfg.incrementalRepairOptions);
280
+
systemd.timers.cassandra-incremental-repair =
281
+
mkIf (!isNull cfg.incrementalRepairInterval) {
282
+
description = "Schedule incremental repairs on Cassandra";
283
+
wantedBy = [ "timers.target" ];
285
+
{ OnBootSec = cfg.incrementalRepairInterval;
286
+
OnUnitActiveSec = cfg.incrementalRepairInterval;