-
Notifications
You must be signed in to change notification settings - Fork 650
Expand file tree
/
Copy pathOptions.cs
More file actions
1111 lines (911 loc) · 63.4 KB
/
Options.cs
File metadata and controls
1111 lines (911 loc) · 63.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using Azure.Identity;
using CommandLine;
using Garnet.common;
using Garnet.server;
using Garnet.server.Auth.Aad;
using Garnet.server.Auth.Settings;
using Garnet.server.TLS;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Exporter;
using Tsavorite.core;
using Tsavorite.devices;
namespace Garnet
{
/// <summary>
/// This class contains all configurable settings for Garnet
/// In order to add a new configurable setting:
/// 1. Add a new property and decorate it with an OptionAttribute.
/// 2. If needed, decorate with a new or existing ValidationAttribute from OptionsValidators.cs
/// 3. Add a default value for the new property in defaults.conf (do NOT use the OptionAttribute.Default property, it can cause unexpected behavior)
/// 4. If needed, add a matching property in Garnet.server/Servers/GarnetServerOptions.cs and initialize it in Options.GetServerOptions()
/// 5. If new setting has a matching setting in redis.conf, add the matching setting to RedisOptions.cs
/// </summary>
internal sealed class Options : ICloneable
{
[IntRangeValidation(0, 65535)]
[Option("port", Required = false, HelpText = "Port to run server on")]
public int Port { get; set; }
[IpAddressValidation(false)]
[Option("bind", Required = false, HelpText = "Whitespace or comma separated string of IP addresses to bind server to (default: any)")]
public string Address { get; set; }
[IntRangeValidation(0, 65535)]
[Option("cluster-announce-port", Required = false, HelpText = "Port that this node advertises to other nodes to connect to for gossiping.")]
public int ClusterAnnouncePort { get; set; }
[IpAddressValidation(false)]
[Option("cluster-announce-ip", Required = false, HelpText = "IP address that this node advertises to other nodes to connect to for gossiping.")]
public string ClusterAnnounceIp { get; set; }
[Option("cluster-announce-hostname", Required = false, HelpText = "Hostname that this node advertises to other nodes to connect to for gossiping.")]
public string ClusterAnnounceHostname { get; set; }
[Option("cluster-preferred-endpoint-type", Required = false, HelpText = "Determines the endpoint type to be advertised to other nodes. (value options: ip, hostname, unknown)")]
public ClusterPreferredEndpointType ClusterPreferredEndpointType { get; set; }
[MemorySizeValidation]
[Option('m', "memory", Required = false, HelpText = "Total log memory used in bytes (rounds down to power of 2)")]
public string MemorySize { get; set; }
[MemorySizeValidation]
[Option('p', "page", Required = false, HelpText = "Size of each page in bytes (rounds down to power of 2)")]
public string PageSize { get; set; }
[MemorySizeValidation]
[Option('s', "segment", Required = false, HelpText = "Size of each log segment in bytes on disk (rounds down to power of 2)")]
public string SegmentSize { get; set; }
[MemorySizeValidation]
[Option('i', "index", Required = false, HelpText = "Start size of hash index in bytes (rounds down to power of 2)")]
public string IndexSize { get; set; }
[MemorySizeValidation(false)]
[Option("index-max-size", Required = false, HelpText = "Max size of hash index in bytes (rounds down to power of 2)")]
public string IndexMaxSize { get; set; }
[PercentageValidation(false)]
[Option("mutable-percent", Required = false, HelpText = "Percentage of log memory that is kept mutable")]
public int MutablePercent { get; set; }
[OptionValidation]
[Option("readcache", Required = false, HelpText = "Enables read cache for faster access to on-disk records.")]
public bool? EnableReadCache { get; set; }
[MemorySizeValidation]
[Option("readcache-memory", Required = false, HelpText = "Total read cache log memory used in bytes (rounds down to power of 2)")]
public string ReadCacheMemorySize { get; set; }
[MemorySizeValidation]
[Option("readcache-page", Required = false, HelpText = "Size of each read cache page in bytes (rounds down to power of 2)")]
public string ReadCachePageSize { get; set; }
[MemorySizeValidation(false)]
[Option("obj-heap-memory", Required = false, HelpText = "Object store heap memory size in bytes (Sum of size taken up by all object instances in the heap)")]
public string ObjectStoreHeapMemorySize { get; set; }
[MemorySizeValidation]
[Option("obj-log-memory", Required = false, HelpText = "Object store log memory used in bytes (Size of only the log with references to heap objects, excludes size of heap memory consumed by the objects themselves referred to from the log)")]
public string ObjectStoreLogMemorySize { get; set; }
[MemorySizeValidation]
[Option("obj-page", Required = false, HelpText = "Size of each object store page in bytes (rounds down to power of 2)")]
public string ObjectStorePageSize { get; set; }
[MemorySizeValidation]
[Option("obj-segment", Required = false, HelpText = "Size of each object store log segment in bytes on disk (rounds down to power of 2)")]
public string ObjectStoreSegmentSize { get; set; }
[MemorySizeValidation]
[Option("obj-index", Required = false, HelpText = "Start size of object store hash index in bytes (rounds down to power of 2)")]
public string ObjectStoreIndexSize { get; set; }
[MemorySizeValidation(false)]
[Option("obj-index-max-size", Required = false, HelpText = "Max size of object store hash index in bytes (rounds down to power of 2)")]
public string ObjectStoreIndexMaxSize { get; set; }
[PercentageValidation]
[Option("obj-mutable-percent", Required = false, HelpText = "Percentage of object store log memory that is kept mutable")]
public int ObjectStoreMutablePercent { get; set; }
[OptionValidation]
[Option("obj-readcache", Required = false, HelpText = "Enables object store read cache for faster access to on-disk records.")]
public bool? EnableObjectStoreReadCache { get; set; }
[MemorySizeValidation]
[Option("obj-readcache-log-memory", Required = false, HelpText = "Total object store read cache log memory used in bytes (rounds down to power of 2)")]
public string ObjectStoreReadCacheLogMemorySize { get; set; }
[MemorySizeValidation]
[Option("obj-readcache-page", Required = false, HelpText = "Size of each object store read cache page in bytes (rounds down to power of 2)")]
public string ObjectStoreReadCachePageSize { get; set; }
[MemorySizeValidation(false)]
[Option("obj-readcache-heap-memory", Required = false, HelpText = "Object store read cache heap memory size in bytes (Sum of size taken up by all object instances in the heap)")]
public string ObjectStoreReadCacheHeapMemorySize { get; set; }
[OptionValidation]
[Option("storage-tier", Required = false, HelpText = "Enable tiering of records (hybrid log) to storage, to support a larger-than-memory store. Use --logdir to specify storage directory.")]
public bool? EnableStorageTier { get; set; }
[OptionValidation]
[Option("copy-reads-to-tail", Required = false, HelpText = "When records are read from the main store's in-memory immutable region or storage device, copy them to the tail of the log.")]
public bool? CopyReadsToTail { get; set; }
[OptionValidation]
[Option("obj-copy-reads-to-tail", Required = false, HelpText = "When records are read from the object store's in-memory immutable region or storage device, copy them to the tail of the log.")]
public bool? ObjectStoreCopyReadsToTail { get; set; }
[LogDirValidation(false, false)]
[Option('l', "logdir", Required = false, HelpText = "Storage directory for tiered records (hybrid log), if storage tiering (--storage-tier) is enabled. Uses current directory if unspecified.")]
public string LogDir { get; set; }
[CheckpointDirValidation(false, false)]
[Option('c', "checkpointdir", Required = false, HelpText = "Storage directory for checkpoints. Uses logdir if unspecified.")]
public string CheckpointDir { get; set; }
[OptionValidation]
[Option('r', "recover", Required = false, HelpText = "Recover from latest checkpoint and log, if present.")]
public bool? Recover { get; set; }
[OptionValidation]
[Option("no-pubsub", Required = false, HelpText = "Disable pub/sub feature on server.")]
public bool? DisablePubSub { get; set; }
[OptionValidation]
[Option("incsnap", Required = false, HelpText = "Enable incremental snapshots.")]
public bool? EnableIncrementalSnapshots { get; set; }
[MemorySizeValidation]
[Option("pubsub-pagesize", Required = false, HelpText = "Page size of log used for pub/sub (rounds down to power of 2)")]
public string PubSubPageSize { get; set; }
[OptionValidation]
[Option("no-obj", Required = false, HelpText = "Disable support for data structure objects.")]
public bool? DisableObjects { get; set; }
[OptionValidation]
[Option("cluster", Required = false, HelpText = "Enable cluster.")]
public bool? EnableCluster { get; set; }
[OptionValidation]
[Option("clean-cluster-config", Required = false, HelpText = "Start with clean cluster config.")]
public bool? CleanClusterConfig { get; set; }
[IntRangeValidation(0, 16384)]
[Option("pmt", Required = false, HelpText = "Number of parallel migrate tasks to spawn when SLOTS or SLOTSRANGE option is used.")]
public int ParallelMigrateTaskCount { get; set; }
[OptionValidation]
[Option("fast-migrate", Required = false, HelpText = "When migrating slots 1. write directly to network buffer to avoid unecessary copies, 2. do not wait for ack from target before sending next batch of keys.")]
public bool? FastMigrate { get; set; }
[Option("auth", Required = false, HelpText = "Authentication mode of Garnet. This impacts how AUTH command is processed and how clients are authenticated against Garnet. Value options: NoAuth, Password, Aad, ACL")]
public GarnetAuthenticationMode AuthenticationMode { get; set; }
[HiddenOption]
[Option("password", Required = false, HelpText = "Authentication string for password authentication.")]
public string Password { get; set; }
[Option("cluster-username", Required = false, HelpText = "Username to authenticate intra-cluster communication with.")]
public string ClusterUsername { get; set; }
[HiddenOption]
[Option("cluster-password", Required = false, HelpText = "Password to authenticate intra-cluster communication with.")]
public string ClusterPassword { get; set; }
[FilePathValidation(true, true, false)]
[Option("acl-file", Required = false, HelpText = "External ACL user file.")]
public string AclFile { get; set; }
[Option("aad-authority", Required = false, HelpText = "The authority of AAD authentication.")]
public string AadAuthority { get; set; }
[Option("aad-audiences", Required = false, HelpText = "The audiences of AAD token for AAD authentication. Should be a comma separated string.")]
public string AadAudiences { get; set; }
[Option("aad-issuers", Required = false, HelpText = "The issuers of AAD token for AAD authentication. Should be a comma separated string.")]
public string AadIssuers { get; set; }
[Option("aad-authorized-app-ids", Required = false, HelpText = "The authorized client app Ids for AAD authentication. Should be a comma separated string.")]
public string AuthorizedAadApplicationIds { get; set; }
[Option("aad-validate-acl-username", Required = false, HelpText = "Only valid for AclWithAAD mode. Validates username - expected to be OID of client app or a valid group's object id of which the client is part of.")]
public bool? AadValidateUsername { get; set; }
[OptionValidation]
[Option("aof", Required = false, HelpText = "Enable write ahead logging (append-only file).")]
public bool? EnableAOF { get; set; }
[MemorySizeValidation]
[Option("aof-memory", Required = false, HelpText = "Total AOF memory buffer used in bytes (rounds down to power of 2) - spills to disk after this limit")]
public string AofMemorySize { get; set; }
[MemorySizeValidation]
[Option("aof-page-size", Required = false, HelpText = "Size of each AOF page in bytes(rounds down to power of 2)")]
public string AofPageSize { get; set; }
[IntRangeValidation(-1, int.MaxValue)]
[Option("aof-commit-freq", Required = false, HelpText = "Write ahead logging (append-only file) commit issue frequency in milliseconds. 0 = issue an immediate commit per operation, -1 = manually issue commits using COMMITAOF command")]
public int CommitFrequencyMs { get; set; }
[OptionValidation]
[Option("aof-commit-wait", Required = false, HelpText = "Wait for AOF to flush the commit before returning results to client. Warning: will greatly increase operation latency.")]
public bool? WaitForCommit { get; set; }
[MemorySizeValidation(false)]
[Option("aof-size-limit", Required = false, HelpText = "Maximum size of AOF (rounds down to power of 2) after which unsafe truncation will be applied. Left empty AOF will grow without bound unless a checkpoint is taken")]
public string AofSizeLimit { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("aof-size-limit-enforce-frequency", Required = false, HelpText = "Frequency (in secs) of execution of the AutoCheckpointBasedOnAofSizeLimit background task.")]
public int AofSizeLimitEnforceFrequencySecs { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("aof-refresh-freq", Required = false, HelpText = "AOF replication (safe tail address) refresh frequency in milliseconds. 0 = auto refresh after every enqueue.")]
public int AofReplicationRefreshFrequencyMs { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("subscriber-refresh-freq", Required = false, HelpText = "Subscriber (safe tail address) refresh frequency in milliseconds (for pub-sub). 0 = auto refresh after every enqueue.")]
public int SubscriberRefreshFrequencyMs { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("compaction-freq", Required = false, HelpText = "Background hybrid log compaction frequency in seconds. 0 = disabled (compaction performed before checkpointing instead)")]
public int CompactionFrequencySecs { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("expired-object-collection-freq", Required = false, HelpText = "Frequency in seconds for the background task to perform object collection which removes expired members within object from memory. 0 = disabled. Use the HCOLLECT and ZCOLLECT API to collect on-demand.")]
public int ExpiredObjectCollectionFrequencySecs { get; set; }
[Option("compaction-type", Required = false, HelpText = "Hybrid log compaction type. Value options: None - no compaction, Shift - shift begin address without compaction (data loss), Scan - scan old pages and move live records to tail (no data loss), Lookup - lookup each record in compaction range, for record liveness checking using hash chain (no data loss)")]
public LogCompactionType CompactionType { get; set; }
[OptionValidation]
[Option("compaction-force-delete", Required = false, HelpText = "Forcefully delete the inactive segments immediately after the compaction strategy (type) is applied. If false, take a checkpoint to actually delete the older data files from disk.")]
public bool? CompactionForceDelete { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("compaction-max-segments", Required = false, HelpText = "Number of log segments created on disk before compaction triggers.")]
public int CompactionMaxSegments { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("obj-compaction-max-segments", Required = false, HelpText = "Number of object store log segments created on disk before compaction triggers.")]
public int ObjectStoreCompactionMaxSegments { get; set; }
[OptionValidation]
[Option("lua", Required = false, HelpText = "Enable Lua scripts on server.")]
public bool? EnableLua { get; set; }
[OptionValidation]
[Option("lua-transaction-mode", Required = false, HelpText = "Run Lua scripts as a transaction (lock keys - run script - unlock keys).")]
public bool? LuaTransactionMode { get; set; }
[PercentageValidation]
[Option("gossip-sp", Required = false, HelpText = "Percent of cluster nodes to gossip with at each gossip iteration.")]
public int GossipSamplePercent { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("gossip-delay", Required = false, HelpText = "Cluster mode gossip protocol per node sleep (in seconds) delay to send updated config.")]
public int GossipDelay { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("cluster-timeout", Required = false, HelpText = "Cluster node timeout is the amount of seconds a node must be unreachable.")]
public int ClusterTimeout { get; set; }
[IntRangeValidation(-1, int.MaxValue)]
[Option("cluster-config-flush-frequency", Required = false, HelpText = "How frequently to flush cluster config unto disk to persist updates. =-1: never (memory only), =0: immediately (every update performs flush), >0: frequency in ms")]
public int ClusterConfigFlushFrequencyMs { get; set; }
[Option("cluster-tls-client-target-host", Required = false, HelpText = "Name for the client target host when using TLS connections in cluster mode.")]
public string ClusterTlsClientTargetHost { get; set; }
[OptionValidation]
[Option("server-certificate-required", Required = false, HelpText = "Whether server TLS certificate is required by clients established on the server side, e.g., for cluster gossip and replication.")]
public bool? ServerCertificateRequired { get; set; }
[OptionValidation]
[Option("tls", Required = false, HelpText = "Enable TLS.")]
public bool? EnableTLS { get; set; }
[CertFileValidation(true, true, false)]
[Option("cert-file-name", Required = false, HelpText = "TLS certificate file name (example: testcert.pfx).")]
public string CertFileName { get; set; }
[HiddenOption]
[Option("cert-password", Required = false, HelpText = "TLS certificate password (example: placeholder).")]
public string CertPassword { get; set; }
[Option("cert-subject-name", Required = false, HelpText = "TLS certificate subject name.")]
public string CertSubjectName { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("cert-refresh-freq", Required = false, HelpText = "TLS certificate refresh frequency in seconds (0 to disable).")]
public int CertificateRefreshFrequency { get; set; }
[OptionValidation]
[Option("client-certificate-required", Required = false, HelpText = "Whether client TLS certificate is required by the server.")]
public bool? ClientCertificateRequired { get; set; }
[Option("certificate-revocation-check-mode", Required = false, HelpText = "Certificate revocation check mode for certificate validation (NoCheck, Online, Offline).")]
public X509RevocationMode CertificateRevocationCheckMode { get; set; }
[Option("issuer-certificate-path", Required = false, HelpText = "Full path of file with issuer certificate for validation. If empty or null, validation against issuer will not be performed.")]
public string IssuerCertificatePath { get; set; }
[OptionValidation]
[Option("latency-monitor", Required = false, HelpText = "Track latency of various events.")]
public bool? LatencyMonitor { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("slowlog-log-slower-than", Required = false, HelpText = "Threshold (microseconds) for logging command in the slow log. 0 to disable.")]
public int SlowLogThreshold { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("slowlog-max-len", Required = false, HelpText = "Maximum number of slow log entries to keep.")]
public int SlowLogMaxEntries { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("metrics-sampling-freq", Required = false, HelpText = "Metrics sampling frequency in seconds. Value of 0 disables metrics monitor task.")]
public int MetricsSamplingFrequency { get; set; }
[Option("opentelemetry-endpoint", Required = false, HelpText = "The endpoint to which OpenTelemetry metrics will be exported. If null, OpenTelemetry metrics will not be exported.")]
public Uri OpenTelemetryEndpoint { get; set; }
[IntRangeValidation(0, int.MaxValue, isRequired: false)]
[Option("opentelemetry-export-interval", Required = false, HelpText = "The interval in milliseconds to export OpenTelemetry metrics. If 0, the default interval of 60 seconds will be used.")]
public int OpenTelemetryExportInterval { get; set; }
[Option("opentelemetry-export-protocol", Required = false, HelpText = "The protocol to use when exporting OpenTelemetry metrics. Value options: Grpc, HttpProtobuf. If null, the default protocol will be used.")]
public OtlpExportProtocol? OpenTelemetryExportProtocol { get; set; }
[IntRangeValidation(0, int.MaxValue, isRequired: false)]
[Option("opentelemetry-export-timeout", Required = false, HelpText = "The timeout in milliseconds when exporting OpenTelemetry metrics. If 0, the default timeout of 10 seconds will be used.")]
public int OpenTelemetryExportTimeout { get; set; }
[OptionValidation]
[Option('q', Required = false, HelpText = "Enabling quiet mode does not print server version and text art.")]
public bool? QuietMode { get; set; }
[Option("logger-level", Required = false, HelpText = "Logging level. Value options: Trace, Debug, Information, Warning, Error, Critical, None")]
public LogLevel LogLevel { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("logger-freq", Required = false, HelpText = "Frequency (in seconds) of logging (used for tracking progress of long running operations e.g. migration)")]
public int LoggingFrequency { get; set; }
[OptionValidation]
[Option("disable-console-logger", Required = false, HelpText = "Disable console logger.")]
public bool? DisableConsoleLogger { get; set; }
[FilePathValidation(false, false, false)]
[Option("file-logger", Required = false, HelpText = "Enable file logger and write to the specified path.")]
public string FileLogger { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("minthreads", Required = false, HelpText = "Minimum worker threads in thread pool, 0 uses the system default.")]
public int ThreadPoolMinThreads { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("maxthreads", Required = false, HelpText = "Maximum worker threads in thread pool, 0 uses the system default.")]
public int ThreadPoolMaxThreads { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("miniothreads", Required = false, HelpText = "Minimum IO completion threads in thread pool, 0 uses the system default.")]
public int ThreadPoolMinIOCompletionThreads { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("maxiothreads", Required = false, HelpText = "Maximum IO completion threads in thread pool, 0 uses the system default.")]
public int ThreadPoolMaxIOCompletionThreads { get; set; }
[IntRangeValidation(-1, int.MaxValue)]
[Option("network-connection-limit", Required = false, HelpText = "Maximum number of simultaneously active network connections.")]
public int NetworkConnectionLimit { get; set; }
[OptionValidation]
[Option("use-azure-storage", Required = false, HelpText = "Use Azure Page Blobs for storage instead of local storage.")]
public bool? UseAzureStorage { get; set; }
[HttpsUrlValidation]
[Option("storage-service-uri", Required = false, HelpText = "The URI to use when establishing connection to Azure Blobs Storage.")]
public string AzureStorageServiceUri { get; set; }
[Option("storage-managed-identity", Required = false, HelpText = "The managed identity to use when establishing connection to Azure Blobs Storage.")]
public string AzureStorageManagedIdentity { get; set; }
[HiddenOption]
[Option("storage-string", Required = false, HelpText = "The connection string to use when establishing connection to Azure Blobs Storage.")]
public string AzureStorageConnectionString { get; set; }
[IntRangeValidation(-1, int.MaxValue)]
[Option("checkpoint-throttle-delay", Required = false, HelpText = "Whether and by how much should we throttle the disk IO for checkpoints: -1 - disable throttling; >= 0 - run checkpoint flush in separate task, sleep for specified time after each WriteAsync")]
public int CheckpointThrottleFlushDelayMs { get; set; }
[OptionValidation]
[Option("fast-commit", Required = false, HelpText = "Use FastCommit when writing AOF.")]
public bool? EnableFastCommit { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("fast-commit-throttle", Required = false, HelpText = "Throttle FastCommit to write metadata once every K commits.")]
public int FastCommitThrottleFreq { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("network-send-throttle", Required = false, HelpText = "Throttle the maximum outstanding network sends per session.")]
public int NetworkSendThrottleMax { get; set; }
[OptionValidation]
[Option("sg-get", Required = false, HelpText = "Whether we use scatter gather IO for MGET or a batch of contiguous GET operations - useful to saturate disk random read IO.")]
public bool? EnableScatterGatherGet { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("replica-sync-delay", Required = false, HelpText = "Whether and by how much (milliseconds) should we throttle the replica sync: 0 - disable throttling")]
public int ReplicaSyncDelayMs { get; set; }
[IntRangeValidation(-1, int.MaxValue)]
[Option("replica-offset-max-lag", Required = false, HelpText = "Throttle ClusterAppendLog when replica.AOFTailAddress - ReplicationOffset > ReplicationOffsetMaxLag. 0: Synchronous replay, >=1: background replay with specified lag, -1: infinite lag")]
public int ReplicationOffsetMaxLag { get; set; }
[OptionValidation]
[Option("main-memory-replication", Required = false, HelpText = "Use main-memory replication model.")]
public bool? MainMemoryReplication { get; set; }
[OptionValidation]
[Option("fast-aof-truncate", Required = false, HelpText = "Use fast-aof-truncate replication model.")]
public bool? FastAofTruncate { get; set; }
[OptionValidation]
[Option("on-demand-checkpoint", Required = false, HelpText = "Used with main-memory replication model. Take on demand checkpoint to avoid missing data when attaching")]
public bool? OnDemandCheckpoint { get; set; }
[OptionValidation]
[Option("repl-diskless-sync", Required = false, HelpText = "Whether diskless replication is enabled or not.")]
public bool? ReplicaDisklessSync { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("repl-diskless-sync-delay", Required = false, HelpText = "Delay in diskless replication sync in seconds. =0: Immediately start diskless replication sync.")]
public int ReplicaDisklessSyncDelay { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("repl-attach-timeout", Required = false, HelpText = "Timeout in seconds for replication attach operation.")]
public int ReplicaAttachTimeout { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("repl-sync-timeout", Required = false, HelpText = "Timeout in seconds for replication sync operations.")]
public int ReplicaSyncTimeout { get; set; }
[MemorySizeValidation(false)]
[Option("repl-diskless-sync-full-sync-aof-threshold", Required = false, HelpText = "AOF replay size threshold for diskless replication, beyond which we will perform a full sync even if a partial sync is possible. Defaults to AOF memory size if not specified.")]
public string ReplicaDisklessSyncFullSyncAofThreshold { get; set; }
[OptionValidation]
[Option("aof-null-device", Required = false, HelpText = "With main-memory replication, use null device for AOF. Ensures no disk IO, but can cause data loss during replication.")]
public bool? UseAofNullDevice { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
[FilePathValidation(true, false, false)]
[Option("config-import-path", Required = false, HelpText = "Import (load) configuration options from the provided path")]
public string ConfigImportPath { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
[Option("config-import-format", Required = false, HelpText = $"Format of configuration options in path specified by config-import-path")]
public ConfigFileType ConfigImportFormat { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
[Option("config-export-format", Required = false, HelpText = $"Format to export configuration options to path specified by config-export-path")]
public ConfigFileType ConfigExportFormat { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
[OptionValidation]
[Option("use-azure-storage-for-config-import", Required = false, Default = false, HelpText = "Use Azure storage to import config file")]
public bool? UseAzureStorageForConfigImport { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
[FilePathValidation(false, false, false)]
[Option("config-export-path", Required = false, HelpText = "Export (save) current configuration options to the provided path")]
public string ConfigExportPath { get; set; }
[System.Text.Json.Serialization.JsonIgnore]
[OptionValidation]
[Option("use-azure-storage-for-config-export", Required = false, Default = false, HelpText = "Use Azure storage to export config file")]
public bool? UseAzureStorageForConfigExport { get; set; }
[OptionValidation]
[Option("use-native-device-linux", Required = false, HelpText = "DEPRECATED: use DeviceType (--device-type) of Native instead.")]
public bool? UseNativeDeviceLinux { get; set; }
[Option("device-type", Required = false, HelpText = "Device type (Default, Native, RandomAccess, FileStream, AzureStorage, Null)")]
public DeviceType DeviceType { get; set; }
[Option("reviv-bin-record-sizes", Separator = ',', Required = false,
HelpText = "#,#,...,#: For the main store, the sizes of records in each revivification bin, in order of increasing size." +
" Supersedes the default --reviv; cannot be used with --reviv-in-chain-only")]
public IEnumerable<int> RevivBinRecordSizes { get; set; }
[Option("reviv-bin-record-counts", Separator = ',', Required = false,
HelpText = "#,#,...,#: For the main store, the number of records in each bin:" +
" Default (not specified): If reviv-bin-record-sizes is specified, each bin is 256 records" +
" # (one value): If reviv-bin-record-sizes is specified, then all bins have this number of records, else error" +
" #,#,...,# (multiple values): If reviv-bin-record-sizes is specified, then it must be the same size as that array, else error" +
" Supersedes the default --reviv; cannot be used with --reviv-in-chain-only")]
public IEnumerable<int> RevivBinRecordCounts { get; set; }
[DoubleRangeValidation(0, 1)]
[Option("reviv-fraction", Required = false,
HelpText = "#: Fraction of mutable in-memory log space, from the highest log address down to the read-only region, that is eligible for revivification." +
" Applies to both main and object store.")]
public double RevivifiableFraction { get; set; }
[OptionValidation]
[Option("reviv", Required = false,
HelpText = "A shortcut to specify revivification with default power-of-2-sized bins." +
" This default can be overridden by --reviv-in-chain-only or by the combination of reviv-bin-record-sizes and reviv-bin-record-counts.")]
public bool? EnableRevivification { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("reviv-search-next-higher-bins", Required = false,
HelpText = "Search this number of next-higher bins if the search cannot be satisfied in the best-fitting bin." +
" Requires --reviv or the combination of rconeviv-bin-record-sizes and reviv-bin-record-counts")]
public int RevivNumberOfBinsToSearch { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("reviv-bin-best-fit-scan-limit", Required = false,
HelpText = "Number of records to scan for best fit after finding first fit." +
" Requires --reviv or the combination of reviv-bin-record-sizes and reviv-bin-record-counts" +
" 0: Use first fit" +
" #: Limit scan to this many records after first fit, up to the record count of the bin")]
public int RevivBinBestFitScanLimit { get; set; }
[OptionValidation]
[Option("reviv-in-chain-only", Required = false,
HelpText = "Revivify tombstoned records in tag chains only (do not use free list)." +
" Cannot be used with reviv-bin-record-sizes or reviv-bin-record-counts. Propagates to object store by default.")]
public bool? RevivInChainOnly { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("reviv-obj-bin-record-count", Required = false,
HelpText = "Number of records in the single free record bin for the object store. The Object store has only a single bin, unlike the main store." +
" Ignored unless the main store is using the free record list.")]
public int RevivObjBinRecordCount { get; set; }
[IntRangeValidation(0, int.MaxValue)]
[Option("object-scan-count-limit", Required = false, HelpText = "Limit of items to return in one iteration of *SCAN command")]
public int ObjectScanCountLimit { get; set; }
[OptionValidation]
[Option("enable-debug-command", Required = false, HelpText = "Enable DEBUG command for 'no', 'local' or 'all' connections")]
public ConnectionProtectionOption EnableDebugCommand { get; set; }
[OptionValidation]
[Option("enable-module-command", Required = false, HelpText = "Enable MODULE command for 'no', 'local' or 'all' connections. Command can only load from paths listed in ExtensionBinPaths")]
public ConnectionProtectionOption EnableModuleCommand { get; set; }
[OptionValidation]
[Option("protected-mode", Required = false, HelpText = "Enable protected mode.")]
public CommandLineBooleanOption ProtectedMode { get; set; }
[DirectoryPathsValidation(true, false)]
[Option("extension-bin-paths", Separator = ',', Required = false, HelpText = "List of directories on server from which custom command binaries can be loaded by admin users. MODULE command also requires enable-module-command to be set")]
public IEnumerable<string> ExtensionBinPaths { get; set; }
[ModuleFilePathValidation(true, true, false)]
[Option("loadmodulecs", Separator = ',', Required = false, HelpText = "List of modules to be loaded")]
public IEnumerable<string> LoadModuleCS { get; set; }
[Option("extension-allow-unsigned", Required = false, HelpText = "Allow loading custom commands from digitally unsigned assemblies (not recommended)")]
public bool? ExtensionAllowUnsignedAssemblies { get; set; }
[IntRangeValidation(1, int.MaxValue, isRequired: false)]
[Option("index-resize-freq", Required = false, HelpText = "Index resize check frequency in seconds")]
public int IndexResizeFrequencySecs { get; set; }
[IntRangeValidation(1, 100, isRequired: false)]
[Option("index-resize-threshold", Required = false, HelpText = "Overflow bucket count over total index size in percentage to trigger index resize")]
public int IndexResizeThreshold { get; set; }
[OptionValidation]
[Option("fail-on-recovery-error", Required = false, HelpText = "Server bootup should fail if errors happen during bootup of AOF and checkpointing")]
public bool? FailOnRecoveryError { get; set; }
[OptionValidation]
[Option("skip-rdb-restore-checksum-validation", Required = false, HelpText = "Skip RDB restore checksum validation")]
public bool? SkipRDBRestoreChecksumValidation { get; set; }
[OptionValidation]
[Option("lua-memory-management-mode", Required = false, HelpText = "Memory management mode for Lua scripts, must be set to Tracked or Managed to impose script limits")]
public LuaMemoryManagementMode LuaMemoryManagementMode { get; set; }
[MemorySizeValidation(false)]
[ForbiddenWithOption(nameof(LuaMemoryManagementMode), nameof(LuaMemoryManagementMode.Native))]
[Option("lua-script-memory-limit", HelpText = "Memory limit for a Lua instances while running a script, lua-memory-management-mode must be set to something other than Native to use this flag")]
public string LuaScriptMemoryLimit { get; set; }
[IntRangeValidation(10, int.MaxValue, isRequired: false)]
[Option("lua-script-timeout", Required = false, HelpText = "Timeout for a Lua instance while running a script, specified in positive milliseconds (0 = disabled)")]
public int LuaScriptTimeoutMs { get; set; }
[OptionValidation]
[Option("lua-logging-mode", Required = false, HelpText = "Behavior of redis.log(...) when called from Lua scripts. Defaults to Enable.")]
public LuaLoggingMode LuaLoggingMode { get; set; }
// Parsing is a tad tricky here as JSON wants to set to empty at certain points
//
// A bespoke union-on-set gets the desired semantics.
private readonly HashSet<string> luaAllowedFunctions = [];
[OptionValidation]
[Option("lua-allowed-functions", Separator = ',', Required = false, HelpText = "If set, restricts the functions available in Lua scripts to given list.")]
public IEnumerable<string> LuaAllowedFunctions
{
get => luaAllowedFunctions;
set
{
if (value == null)
{
return;
}
luaAllowedFunctions.UnionWith(value);
}
}
[FilePathValidation(false, true, false)]
[Option("unixsocket", Required = false, HelpText = "Unix socket address path to bind server to")]
public string UnixSocketPath { get; set; }
[IntRangeValidation(0, 777, isRequired: false)]
[SupportedOSValidation(isRequired: false, nameof(OSPlatform.Linux), nameof(OSPlatform.OSX), nameof(OSPlatform.FreeBSD))]
[Option("unixsocketperm", Required = false, HelpText = "Unix socket permissions in octal (Unix platforms only)")]
public int UnixSocketPermission { get; set; }
[IntRangeValidation(1, 256, isRequired: true)]
[Option("max-databases", Required = false, HelpText = "Max number of logical databases allowed in a single Garnet server instance")]
public int MaxDatabases { get; set; }
[IntRangeValidation(-1, int.MaxValue, isRequired: false)]
[Option("expired-key-deletion-scan-freq", Required = false, HelpText = "Frequency of background scan for expired key deletion, in seconds")]
public int ExpiredKeyDeletionScanFrequencySecs { get; set; }
[IntRangeValidation(0, int.MaxValue, includeMin: true, isRequired: false)]
[Option("cluster-replication-reestablishment-timeout")]
public int ClusterReplicationReestablishmentTimeout { get; set; }
[Option("cluster-replica-resume-with-data", Required = false, HelpText = "If a Cluster Replica resumes with data, allow it to be served prior to a Primary being available")]
public bool ClusterReplicaResumeWithData { get; set; }
[RequiresMinimumMemory(nameof(PageSize), minimumValue: "16K")]
[Option("enable-vector-set-preview", Required = false, HelpText = "Enable Vector Sets (preview) - this feature (and associated commands) are incomplete, unstable, and subject to change while still in preview")]
public bool EnableVectorSetPreview { get; set; }
/// <summary>
/// This property contains all arguments that were not parsed by the command line argument parser
/// </summary>
[System.Text.Json.Serialization.JsonIgnore]
[Value(0)]
public IList<string> UnparsedArguments { get; set; }
/// <summary>
/// Logger instance used for runtime option validation
/// </summary>
public ILogger runtimeLogger { get; set; }
/// <summary>
/// Check the validity of all options with an explicit ValidationAttribute
/// </summary>
/// <param name="invalidOptions">List of invalid options</param>
/// <param name="logger">Logger</param>
/// <returns>True if all property values are valid</returns>
public bool IsValid(out List<string> invalidOptions, ILogger logger = null)
{
invalidOptions = [];
var isValid = true;
this.runtimeLogger = logger;
foreach (var prop in typeof(Options).GetProperties())
{
// Ignore if property is not decorated with the OptionsAttribute or the ValidationAttribute
var validationAttr = prop.GetCustomAttributes(typeof(ValidationAttribute)).FirstOrDefault();
if (!Attribute.IsDefined(prop, typeof(OptionAttribute)) || validationAttr == null)
continue;
// Get value to validate and set validation context
var value = prop.GetValue(this, null);
var validationContext = new ValidationContext(this) { MemberName = prop.Name };
// Validate the current value
var validationResults = new List<ValidationResult>();
var isValueValid = Validator.TryValidateProperty(value, validationContext, validationResults);
// Append results
isValid = isValid && isValueValid;
foreach (var validationResult in validationResults)
{
invalidOptions.AddRange(validationResult.MemberNames);
logger?.LogError("{errorMessage}", validationResult.ErrorMessage);
}
}
return isValid;
}
public GarnetServerOptions GetServerOptions(ILogger logger = null)
{
var enableStorageTier = EnableStorageTier.GetValueOrDefault();
var enableRevivification = EnableRevivification.GetValueOrDefault();
if (UseNativeDeviceLinux.GetValueOrDefault())
{
logger?.LogWarning("The --use-native-device-linux option is deprecated. Please use --device-type Native instead.");
DeviceType = DeviceType.Native;
}
var deviceType = GetDeviceType(logger);
var useAzureStorage = deviceType == DeviceType.AzureStorage;
if (useAzureStorage && string.IsNullOrEmpty(AzureStorageConnectionString) && string.IsNullOrEmpty(AzureStorageServiceUri))
{
throw new InvalidAzureConfiguration("Cannot use AzureStorage device without supplying storage-string or storage-service-uri");
}
if (useAzureStorage && !string.IsNullOrEmpty(AzureStorageConnectionString) && !string.IsNullOrEmpty(AzureStorageServiceUri))
{
throw new InvalidAzureConfiguration("Cannot use AzureStorage device with both storage-string and storage-service-uri");
}
var logDir = LogDir;
if (!useAzureStorage && enableStorageTier) logDir = new DirectoryInfo(string.IsNullOrEmpty(logDir) ? "." : logDir).FullName;
var checkpointDir = CheckpointDir;
if (!useAzureStorage) checkpointDir = new DirectoryInfo(string.IsNullOrEmpty(checkpointDir) ? (string.IsNullOrEmpty(logDir) ? "." : logDir) : checkpointDir).FullName;
if (!Format.TryParseAddressList(Address, Port, out var endpoints, out _, ProtectedMode == CommandLineBooleanOption.True)
|| endpoints.Length == 0)
throw new GarnetException($"Invalid endpoint format {Address} {Port}.");
EndPoint[] clusterAnnounceEndpoint = null;
if (ClusterAnnounceIp != null)
{
ClusterAnnouncePort = ClusterAnnouncePort == 0 ? Port : ClusterAnnouncePort;
clusterAnnounceEndpoint = Format.TryCreateEndpoint(ClusterAnnounceIp, ClusterAnnouncePort, tryConnect: false, logger: logger).GetAwaiter().GetResult();
if (clusterAnnounceEndpoint == null || !endpoints.Any(endpoint => endpoint.Equals(clusterAnnounceEndpoint[0])))
throw new GarnetException("Cluster announce endpoint does not match list of listen endpoints provided!");
}
if (!string.IsNullOrEmpty(UnixSocketPath))
endpoints = [.. endpoints, new UnixDomainSocketEndPoint(UnixSocketPath)];
// Unix file permission octal to UnixFileMode
var unixSocketPermissions = (UnixFileMode)Convert.ToInt32(UnixSocketPermission.ToString(), 8);
var revivBinRecordSizes = this.RevivBinRecordSizes?.ToArray();
var revivBinRecordCounts = this.RevivBinRecordCounts?.ToArray();
bool hasRecordSizes = revivBinRecordSizes?.Length > 0, hasRecordCounts = revivBinRecordCounts?.Length > 0;
bool useRevivBinsPowerOf2 = enableRevivification; // may be overridden
if (hasRecordSizes)
{
if (hasRecordCounts && revivBinRecordCounts.Length > 1 && revivBinRecordCounts.Length != revivBinRecordSizes.Length)
throw new Exception("Incompatible revivification record size and count cardinality.");
if (RevivInChainOnly.GetValueOrDefault())
throw new Exception("Revivification cannot specify both record sizes and in-chain-only.");
useRevivBinsPowerOf2 = false;
}
if (hasRecordCounts)
{
if (enableRevivification)
throw new Exception("Revivification cannot specify both record counts and powerof2 bins.");
if (!hasRecordSizes)
throw new Exception("Revivification bin counts require bin sizes.");
useRevivBinsPowerOf2 = false;
}
if (RevivBinBestFitScanLimit != 0)
{
if (!hasRecordSizes && !enableRevivification)
throw new Exception("Revivification cannot specify best fit scan limit without specifying bins.");
if (RevivBinBestFitScanLimit < 0)
throw new Exception("RevivBinBestFitScanLimit must be >= 0.");
}
if (RevivifiableFraction != RevivificationSettings.DefaultRevivifiableFraction)
{
if (!hasRecordSizes && !enableRevivification)
throw new Exception("Revivification cannot specify RevivifiableFraction without specifying bins.");
}
// For backwards compatibility
if (CompactionType == LogCompactionType.ShiftForced)
{
logger?.LogWarning("Compaction type ShiftForced is deprecated. Use Shift instead along with CompactionForceDelete.");
CompactionType = LogCompactionType.Shift;
CompactionForceDelete = true;
}
if (SlowLogThreshold > 0)
{
if (SlowLogThreshold < 100)
throw new Exception("SlowLogThreshold must be at least 100 microseconds.");
}
Func<INamedDeviceFactoryCreator> azureFactoryCreator = () =>
{
if (!string.IsNullOrEmpty(AzureStorageConnectionString))
{
return new AzureStorageNamedDeviceFactoryCreator(AzureStorageConnectionString, logger);
}
var credential = new ChainedTokenCredential(
new WorkloadIdentityCredential(),
new ManagedIdentityCredential(clientId: AzureStorageManagedIdentity)
);
return new AzureStorageNamedDeviceFactoryCreator(AzureStorageServiceUri, credential, logger);
};
return new GarnetServerOptions(logger)
{
EndPoints = endpoints,
ClusterAnnounceEndpoint = clusterAnnounceEndpoint?[0],
ClusterAnnounceHostname = ClusterAnnounceHostname,
ClusterPreferredEndpointType = ClusterPreferredEndpointType,
MemorySize = MemorySize,
PageSize = PageSize,
SegmentSize = SegmentSize,
IndexSize = IndexSize,
IndexMaxSize = IndexMaxSize,
MutablePercent = MutablePercent,
EnableReadCache = EnableReadCache.GetValueOrDefault(),
ReadCacheMemorySize = ReadCacheMemorySize,
ReadCachePageSize = ReadCachePageSize,
ObjectStoreHeapMemorySize = ObjectStoreHeapMemorySize,
ObjectStoreLogMemorySize = ObjectStoreLogMemorySize,
ObjectStorePageSize = ObjectStorePageSize,
ObjectStoreSegmentSize = ObjectStoreSegmentSize,
ObjectStoreIndexSize = ObjectStoreIndexSize,
ObjectStoreIndexMaxSize = ObjectStoreIndexMaxSize,
ObjectStoreMutablePercent = ObjectStoreMutablePercent,
EnableObjectStoreReadCache = EnableObjectStoreReadCache.GetValueOrDefault(),
ObjectStoreReadCachePageSize = ObjectStoreReadCachePageSize,
ObjectStoreReadCacheLogMemorySize = ObjectStoreReadCacheLogMemorySize,
ObjectStoreReadCacheHeapMemorySize = ObjectStoreReadCacheHeapMemorySize,
EnableStorageTier = enableStorageTier,
CopyReadsToTail = CopyReadsToTail.GetValueOrDefault(),
ObjectStoreCopyReadsToTail = ObjectStoreCopyReadsToTail.GetValueOrDefault(),
LogDir = logDir,
CheckpointDir = checkpointDir,
Recover = Recover.GetValueOrDefault(),
EnableIncrementalSnapshots = EnableIncrementalSnapshots.GetValueOrDefault(),
DisablePubSub = DisablePubSub.GetValueOrDefault(),
PubSubPageSize = PubSubPageSize,
DisableObjects = DisableObjects.GetValueOrDefault(),
EnableCluster = EnableCluster.GetValueOrDefault(),
CleanClusterConfig = CleanClusterConfig.GetValueOrDefault(),
ParallelMigrateTaskCount = ParallelMigrateTaskCount,
FastMigrate = FastMigrate.GetValueOrDefault(),
AuthSettings = GetAuthenticationSettings(logger),
EnableAOF = EnableAOF.GetValueOrDefault(),
EnableLua = EnableLua.GetValueOrDefault(),
LuaTransactionMode = LuaTransactionMode.GetValueOrDefault(),
AofMemorySize = AofMemorySize,
AofPageSize = AofPageSize,
AofReplicationRefreshFrequencyMs = AofReplicationRefreshFrequencyMs,
CommitFrequencyMs = CommitFrequencyMs,
WaitForCommit = WaitForCommit.GetValueOrDefault(),
AofSizeLimit = AofSizeLimit,
AofSizeLimitEnforceFrequencySecs = AofSizeLimitEnforceFrequencySecs,
CompactionFrequencySecs = CompactionFrequencySecs,
ExpiredObjectCollectionFrequencySecs = ExpiredObjectCollectionFrequencySecs,
CompactionType = CompactionType,
CompactionForceDelete = CompactionForceDelete.GetValueOrDefault(),
CompactionMaxSegments = CompactionMaxSegments,
ObjectStoreCompactionMaxSegments = ObjectStoreCompactionMaxSegments,
GossipSamplePercent = GossipSamplePercent,
GossipDelay = GossipDelay,
ClusterTimeout = ClusterTimeout,
ClusterConfigFlushFrequencyMs = ClusterConfigFlushFrequencyMs,
EnableFastCommit = EnableFastCommit.GetValueOrDefault(),
FastCommitThrottleFreq = FastCommitThrottleFreq,
NetworkSendThrottleMax = NetworkSendThrottleMax,
TlsOptions = EnableTLS.GetValueOrDefault() ? new GarnetTlsOptions(
CertFileName, CertPassword,
ClientCertificateRequired.GetValueOrDefault(),
CertificateRevocationCheckMode,
IssuerCertificatePath,
CertSubjectName,
CertificateRefreshFrequency,
EnableCluster.GetValueOrDefault(),
ClusterTlsClientTargetHost,
ServerCertificateRequired.GetValueOrDefault(),
logger: logger) : null,
LatencyMonitor = LatencyMonitor.GetValueOrDefault(),
SlowLogThreshold = SlowLogThreshold,
SlowLogMaxEntries = SlowLogMaxEntries,
MetricsSamplingFrequency = MetricsSamplingFrequency,
OpenTelemetryEndpoint = OpenTelemetryEndpoint,
OpenTelemetryExportInterval = OpenTelemetryExportInterval,
OpenTelemetryExportProtocol = OpenTelemetryExportProtocol,
OpenTelemetryExportTimeout = OpenTelemetryExportTimeout,
LogLevel = LogLevel,
LoggingFrequency = LoggingFrequency,
QuietMode = QuietMode.GetValueOrDefault(),
ThreadPoolMinThreads = ThreadPoolMinThreads,
ThreadPoolMaxThreads = ThreadPoolMaxThreads,
ThreadPoolMinIOCompletionThreads = ThreadPoolMinIOCompletionThreads,
ThreadPoolMaxIOCompletionThreads = ThreadPoolMaxIOCompletionThreads,
NetworkConnectionLimit = NetworkConnectionLimit,
DeviceFactoryCreator = deviceType == DeviceType.AzureStorage ? azureFactoryCreator()
: new LocalStorageNamedDeviceFactoryCreator(deviceType: deviceType, logger: logger),
CheckpointThrottleFlushDelayMs = CheckpointThrottleFlushDelayMs,
EnableScatterGatherGet = EnableScatterGatherGet.GetValueOrDefault(),
ReplicaSyncDelayMs = ReplicaSyncDelayMs,
ReplicationOffsetMaxLag = ReplicationOffsetMaxLag,
FastAofTruncate = GetFastAofTruncate(logger),
OnDemandCheckpoint = OnDemandCheckpoint.GetValueOrDefault(),
ReplicaDisklessSync = ReplicaDisklessSync.GetValueOrDefault(),
ReplicaDisklessSyncDelay = ReplicaDisklessSyncDelay,
ReplicaSyncTimeout = ReplicaSyncTimeout <= 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(ReplicaSyncTimeout),
ReplicaAttachTimeout = ReplicaAttachTimeout <= 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(ReplicaAttachTimeout),
ReplicaDisklessSyncFullSyncAofThreshold = ReplicaDisklessSyncFullSyncAofThreshold,
UseAofNullDevice = UseAofNullDevice.GetValueOrDefault(),
ClusterUsername = ClusterUsername,
ClusterPassword = ClusterPassword,
DeviceType = deviceType,
ObjectScanCountLimit = ObjectScanCountLimit,
RevivBinRecordSizes = revivBinRecordSizes,
RevivBinRecordCounts = revivBinRecordCounts,
RevivifiableFraction = RevivifiableFraction,
UseRevivBinsPowerOf2 = useRevivBinsPowerOf2,
RevivBinBestFitScanLimit = RevivBinBestFitScanLimit,
RevivNumberOfBinsToSearch = RevivNumberOfBinsToSearch,
RevivInChainOnly = RevivInChainOnly.GetValueOrDefault(),
RevivObjBinRecordCount = RevivObjBinRecordCount,
EnableDebugCommand = EnableDebugCommand,
EnableModuleCommand = EnableModuleCommand,
ExtensionBinPaths = FileUtils.ConvertToAbsolutePaths(ExtensionBinPaths),
ExtensionAllowUnsignedAssemblies = ExtensionAllowUnsignedAssemblies.GetValueOrDefault(),
IndexResizeFrequencySecs = IndexResizeFrequencySecs,
IndexResizeThreshold = IndexResizeThreshold,
LoadModuleCS = LoadModuleCS,
FailOnRecoveryError = FailOnRecoveryError.GetValueOrDefault(),
SkipRDBRestoreChecksumValidation = SkipRDBRestoreChecksumValidation.GetValueOrDefault(),
LuaOptions = EnableLua.GetValueOrDefault() ? new LuaOptions(LuaMemoryManagementMode, LuaScriptMemoryLimit, LuaScriptTimeoutMs == 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromMilliseconds(LuaScriptTimeoutMs), LuaLoggingMode, LuaAllowedFunctions, logger) : null,
UnixSocketPath = UnixSocketPath,
UnixSocketPermission = unixSocketPermissions,
MaxDatabases = MaxDatabases,
ExpiredKeyDeletionScanFrequencySecs = ExpiredKeyDeletionScanFrequencySecs,
ClusterReplicationReestablishmentTimeout = ClusterReplicationReestablishmentTimeout,
ClusterReplicaResumeWithData = ClusterReplicaResumeWithData,
EnableVectorSetPreview = EnableVectorSetPreview,
};
}
internal DeviceType GetDeviceType(ILogger logger = null)
{
var deviceType = DeviceType;
if (deviceType == DeviceType.Default)
{
deviceType = Devices.GetDefaultDeviceType();
}
if (UseAzureStorage.GetValueOrDefault())
{
logger?.LogInformation("The UseAzureStorage flag is deprecated, use DeviceType of AzureStorage instead");