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
use std::sync::OnceLock;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// Global feature flags, normally initialized when starting Qdrant.
static FEATURE_FLAGS: OnceLock<FeatureFlags> = OnceLock::new();
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Eq, PartialEq, JsonSchema)]
#[serde(default)]
pub struct FeatureFlags {
/// Magic feature flag that enables all features.
///
/// Note that this will only be applied to all flags when passed into [`init_feature_flags`].
all: bool,
/// Use incremental HNSW building.
///
/// Enabled by default in Qdrant 1.14.1.
pub incremental_hnsw_building: bool,
/// Use appendable quantization in appendable plain segments.
///
/// Enabled by default in Qdrant 1.16.0.
pub appendable_quantization: bool,
/// Use single-file mmap in-ram vector storage (InRamMmap)
///
/// Enabled by default in Qdrant 1.18.3+
pub single_file_mmap_vector_storage: bool,
/// Allow the io_uring-based payload storage implementation.
/// When disabled, io_uring payload storage is *never* used.
/// When enabled, payload storage backend is decided based on `storage.performance.io_uring` option and payload storage type.
pub async_payload_storage: bool,
/// Write a segment manifest (`segments_manifest.json`, next to the `segments/` directory)
/// listing the shard's segments and their
/// state, so out-of-process readers can discover segments without scanning the filesystem.
pub write_segment_manifest: bool,
/// Build new segments in append-only mode: in-place point mutations become clone-and-tombstone
/// appends instead. Intended for testing the append-only storage path.
pub append_only_mutations: bool,
/// Persist write-once bitmasks in the compact `StoredBitmask` format instead of raw dense
/// bitslices. Only gates writing: both formats are always readable.
pub compact_bitmask: bool,
/// Serverless-compatible deployment mode. Automatically enables [`Self::write_segment_manifest`],
/// [`Self::append_only_mutations`] and [`Self::compact_bitmask`].
///
/// Note that this will only be applied when passed into [`init_feature_flags`].
serverless_compatible: bool,
}
impl Default for FeatureFlags {
fn default() -> FeatureFlags {
FeatureFlags {
all: false,
incremental_hnsw_building: true,
appendable_quantization: true,
single_file_mmap_vector_storage: true,
async_payload_storage: true,
write_segment_manifest: false,
append_only_mutations: false,
compact_bitmask: false,
serverless_compatible: false,
}
}
}
impl FeatureFlags {
/// Check if the feature flags are set to default values.
pub fn is_default(self) -> bool {
self == FeatureFlags::default()
}
/// Whether segments should be produced in a serverless-compatible way (e.g.
/// the disk-resident id-tracker format). See the field docs for implications.
pub fn serverless_compatible(self) -> bool {
self.serverless_compatible
}
fn all() -> Self {
Self {
all: true,
incremental_hnsw_building: true,
appendable_quantization: true,
single_file_mmap_vector_storage: true,
async_payload_storage: true,
write_segment_manifest: true,
// Deliberately not enabled by `all`: this is a test-only escape hatch that changes
// mutation semantics, and `all` is enabled in dev and e2e configs.
append_only_mutations: false,
compact_bitmask: true,
serverless_compatible: false,
}
}
fn normalize(mut self) -> Self {
let serverless_compatible = self.serverless_compatible;
if self.all {
self = Self::all();
}
if serverless_compatible {
self.serverless_compatible = true;
self.write_segment_manifest = true;
self.append_only_mutations = true;
self.compact_bitmask = true;
}
self
}
}
/// Initializes the global feature flags with `flags`. Must only be called once at
/// startup or otherwise throws a warning and discards the values.
pub fn init_feature_flags(flags: FeatureFlags) {
let flags = flags.normalize();
let res = FEATURE_FLAGS.set(flags);
if res.is_err() {
log::warn!("Feature flags already initialized!");
}
}
/// Returns the configured global feature flags.
pub fn feature_flags() -> FeatureFlags {
if let Some(flags) = FEATURE_FLAGS.get() {
return *flags;
}
// They should always be initialized.
log::warn!("Feature flags not initialized!");
FeatureFlags::default()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_defaults() {
// Ensure we properly deserialize and don't crash on empty state
let empty: FeatureFlags = serde_json::from_str("{}").unwrap();
assert!(empty.is_default());
assert!(feature_flags().is_default());
assert!(FeatureFlags::default().is_default());
}
#[test]
fn test_serverless_compatible_enables_sub_flags() {
let flags = FeatureFlags {
serverless_compatible: true,
..Default::default()
}
.normalize();
assert!(flags.write_segment_manifest);
assert!(flags.append_only_mutations);
assert!(flags.compact_bitmask);
}
#[test]
fn test_serverless_compatible_after_all() {
let flags = FeatureFlags {
all: true,
serverless_compatible: true,
..Default::default()
}
.normalize();
assert!(flags.write_segment_manifest);
assert!(flags.append_only_mutations);
assert!(flags.compact_bitmask);
}
}