1use std::collections::{HashMap, VecDeque};
16use std::mem;
17use std::num::NonZeroUsize;
18use std::ops::RangeInclusive;
19use std::sync::Arc;
20use std::sync::atomic::{AtomicUsize, Ordering};
21
22use async_trait::async_trait;
23use common_telemetry::{debug, error};
24use futures::stream;
25use roaring::RoaringBitmap;
26use snafu::ResultExt;
27
28use crate::bitmap::Bitmap;
29use crate::external_provider::ExternalTempFileProvider;
30use crate::inverted_index::create::sort::intermediate_rw::{
31 IntermediateReader, IntermediateWriter,
32};
33use crate::inverted_index::create::sort::merge_stream::MergeSortedStream;
34use crate::inverted_index::create::sort::{SortOutput, SortedStream, Sorter};
35use crate::inverted_index::create::sort_create::SorterFactory;
36use crate::inverted_index::error::{IntermediateSnafu, Result};
37use crate::{Bytes, BytesRef};
38
39struct Posting {
41 segments: RoaringBitmap,
42 last_segment: u32,
43}
44
45const SEGMENT_SIZE: usize = size_of::<u16>();
47
48impl Posting {
49 fn push(&mut self, start: u32, end: u32) -> usize {
51 if end <= self.last_segment {
52 return 0;
53 }
54 let start = start.max(self.last_segment + 1);
55 self.segments.insert_range(start..=end);
56 self.last_segment = end;
57 (end - start + 1) as usize * SEGMENT_SIZE
58 }
59}
60
61pub struct ExternalSorter {
64 index_name: String,
66
67 temp_file_provider: Arc<dyn ExternalTempFileProvider>,
69
70 segment_null_bitmap: Bitmap,
72
73 values_buffer: HashMap<Bytes, Posting, ahash::RandomState>,
75
76 total_row_count: usize,
78
79 segment_row_count: NonZeroUsize,
82
83 current_memory_usage: usize,
85
86 current_memory_usage_threshold: Option<usize>,
91
92 global_memory_usage: Arc<AtomicUsize>,
94
95 global_memory_usage_sort_limit: Option<usize>,
100}
101
102#[async_trait]
103impl Sorter for ExternalSorter {
104 fn push_n(&mut self, value: Option<BytesRef<'_>>, n: usize) -> bool {
107 if n == 0 {
108 return false;
109 }
110
111 let segment_index_range = self.segment_index_range(n);
112 self.total_row_count += n;
113
114 if let Some(value) = value {
115 let memory_diff = self.push_not_null(value, segment_index_range);
116 self.account_memory(memory_diff)
117 } else {
118 self.segment_null_bitmap.insert_range(segment_index_range);
119 false
120 }
121 }
122
123 async fn spill(&mut self) -> Result<()> {
124 self.dump_buffer().await
125 }
126
127 async fn output(&mut self) -> Result<SortOutput> {
130 let readers = self
131 .temp_file_provider
132 .read_all(&self.index_name)
133 .await
134 .context(IntermediateSnafu)?;
135
136 let mut tree_nodes: VecDeque<SortedStream> = VecDeque::with_capacity(readers.len() + 1);
139 tree_nodes.push_back(Box::new(stream::iter(
140 Self::sorted(mem::take(&mut self.values_buffer)).map(Ok),
141 )));
142 for (_, reader) in readers {
143 tree_nodes.push_back(IntermediateReader::new(reader).into_stream().await?);
144 }
145
146 while tree_nodes.len() >= 2 {
147 let stream1 = tree_nodes.pop_front().unwrap();
149 let stream2 = tree_nodes.pop_front().unwrap();
150 let merged_stream = MergeSortedStream::merge(stream1, stream2);
151 tree_nodes.push_back(merged_stream);
152 }
153
154 Ok(SortOutput {
155 segment_null_bitmap: mem::take(&mut self.segment_null_bitmap),
156 sorted_stream: tree_nodes.pop_front().unwrap(),
157 total_row_count: self.total_row_count,
158 })
159 }
160}
161
162impl ExternalSorter {
163 pub fn new(
165 index_name: String,
166 temp_file_provider: Arc<dyn ExternalTempFileProvider>,
167 segment_row_count: NonZeroUsize,
168 current_memory_usage_threshold: Option<usize>,
169 global_memory_usage: Arc<AtomicUsize>,
170 global_memory_usage_sort_limit: Option<usize>,
171 ) -> Self {
172 Self {
173 index_name,
174 temp_file_provider,
175
176 segment_null_bitmap: Bitmap::new_bitvec(), values_buffer: HashMap::default(),
178
179 total_row_count: 0,
180 segment_row_count,
181
182 current_memory_usage: 0,
183 current_memory_usage_threshold,
184 global_memory_usage,
185 global_memory_usage_sort_limit,
186 }
187 }
188
189 pub fn factory(
191 temp_file_provider: Arc<dyn ExternalTempFileProvider>,
192 current_memory_usage_threshold: Option<usize>,
193 global_memory_usage: Arc<AtomicUsize>,
194 global_memory_usage_sort_limit: Option<usize>,
195 ) -> SorterFactory {
196 Box::new(move |index_name, segment_row_count| {
197 Box::new(Self::new(
198 index_name,
199 temp_file_provider.clone(),
200 segment_row_count,
201 current_memory_usage_threshold,
202 global_memory_usage.clone(),
203 global_memory_usage_sort_limit,
204 ))
205 })
206 }
207
208 fn push_not_null(
212 &mut self,
213 value: BytesRef<'_>,
214 segment_index_range: RangeInclusive<usize>,
215 ) -> usize {
216 let (start, end) = (
217 *segment_index_range.start() as u32,
218 *segment_index_range.end() as u32,
219 );
220 match self.values_buffer.get_mut(value) {
221 Some(posting) => posting.push(start, end),
222 None => {
223 let mut segments = RoaringBitmap::new();
224 segments.insert_range(start..=end);
225 let posting = Posting {
226 segments,
227 last_segment: end,
228 };
229 self.values_buffer.insert(value.to_vec(), posting);
230 value.len() + (end - start + 1) as usize * SEGMENT_SIZE
231 }
232 }
233 }
234
235 fn sorted(
237 values: HashMap<Bytes, Posting, ahash::RandomState>,
238 ) -> impl Iterator<Item = (Bytes, Bitmap)> {
239 let mut values = values.into_iter().collect::<Vec<_>>();
240 values.sort_unstable_by(|a, b| a.0.cmp(&b.0));
241 values
242 .into_iter()
243 .map(|(value, posting)| (value, Bitmap::Roaring(posting.segments)))
244 }
245
246 fn account_memory(&mut self, memory_diff: usize) -> bool {
249 self.current_memory_usage += memory_diff;
250 let memory_usage = self.current_memory_usage;
251 self.global_memory_usage
252 .fetch_add(memory_diff, Ordering::Relaxed);
253
254 let Some(limit) = self.global_memory_usage_sort_limit else {
255 return false;
256 };
257 if self.global_memory_usage.load(Ordering::Relaxed) < limit {
258 return false;
259 }
260 if let Some(current_threshold) = self.current_memory_usage_threshold
261 && memory_usage < current_threshold
262 {
263 return false;
264 }
265 true
266 }
267
268 async fn dump_buffer(&mut self) -> Result<()> {
269 if self.values_buffer.is_empty() {
272 return Ok(());
273 }
274 let memory_usage = self.current_memory_usage;
275 let file_id = &format!("{:012}", self.total_row_count);
276 let index_name = &self.index_name;
277 let writer = self
278 .temp_file_provider
279 .create(index_name, file_id)
280 .await
281 .context(IntermediateSnafu)?;
282
283 let values = mem::take(&mut self.values_buffer);
284 self.global_memory_usage
285 .fetch_sub(memory_usage, Ordering::Relaxed);
286 self.current_memory_usage = 0;
287
288 let entries = values.len();
289 IntermediateWriter::new(writer).write_all(Self::sorted(values)).await.inspect(|_|
290 debug!("Dumped {entries} entries ({memory_usage} bytes) to intermediate file {file_id} for index {index_name}")
291 ).inspect_err(|e|
292 error!(e; "Failed to dump {entries} entries to intermediate file {file_id} for index {index_name}")
293 )
294 }
295
296 fn segment_index_range(&self, n: usize) -> RangeInclusive<usize> {
299 let row_begin = self.total_row_count;
300 let start = self.segment_index(row_begin);
301 let end = self.segment_index(row_begin + n - 1);
302 start..=end
303 }
304
305 fn segment_index(&self, row_index: usize) -> usize {
307 row_index / self.segment_row_count
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use std::collections::{BTreeMap, HashMap};
314 use std::iter;
315 use std::sync::Mutex;
316
317 use futures::{AsyncRead, StreamExt};
318 use rand::Rng;
319 use tokio::io::duplex;
320 use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
321
322 use super::*;
323 use crate::external_provider::MockExternalTempFileProvider;
324
325 async fn test_external_sorter(
326 current_memory_usage_threshold: Option<usize>,
327 global_memory_usage_sort_limit: Option<usize>,
328 segment_row_count: usize,
329 row_count: usize,
330 batch_push: bool,
331 ) {
332 let mut mock_provider = MockExternalTempFileProvider::new();
333
334 let mock_files: Arc<Mutex<HashMap<String, Box<dyn AsyncRead + Unpin + Send>>>> =
335 Arc::new(Mutex::new(HashMap::new()));
336
337 mock_provider.expect_create().returning({
338 let files = Arc::clone(&mock_files);
339 move |index_name, file_id| {
340 assert_eq!(index_name, "test");
341 let mut files = files.lock().unwrap();
342 let (writer, reader) = duplex(1024 * 1024);
343 files.insert(file_id.to_string(), Box::new(reader.compat()));
344 Ok(Box::new(writer.compat_write()))
345 }
346 });
347
348 mock_provider.expect_read_all().returning({
349 let files = Arc::clone(&mock_files);
350 move |index_name| {
351 assert_eq!(index_name, "test");
352 let mut files = files.lock().unwrap();
353 Ok(files.drain().collect::<Vec<_>>())
354 }
355 });
356
357 let mut sorter = ExternalSorter::new(
358 "test".to_owned(),
359 Arc::new(mock_provider),
360 NonZeroUsize::new(segment_row_count).unwrap(),
361 current_memory_usage_threshold,
362 Arc::new(AtomicUsize::new(0)),
363 global_memory_usage_sort_limit,
364 );
365
366 let mut sorted_result = if batch_push {
367 let (dic_values, sorted_result) =
368 dictionary_values_and_sorted_result(row_count, segment_row_count);
369
370 for (value, n) in dic_values {
371 if sorter.push_n(value.as_deref(), n) {
372 sorter.spill().await.unwrap();
373 }
374 }
375
376 sorted_result
377 } else {
378 let (mock_values, sorted_result) =
379 shuffle_values_and_sorted_result(row_count, segment_row_count);
380
381 for value in mock_values {
382 if sorter.push_n(value.as_deref(), 1) {
383 sorter.spill().await.unwrap();
384 }
385 }
386
387 sorted_result
388 };
389
390 let SortOutput {
391 segment_null_bitmap,
392 mut sorted_stream,
393 total_row_count,
394 } = sorter.output().await.unwrap();
395 assert_eq!(total_row_count, row_count);
396 let n = sorted_result.remove(&None);
397 assert_eq!(
398 segment_null_bitmap.iter_ones().collect::<Vec<_>>(),
399 n.unwrap_or_default()
400 );
401 for (value, offsets) in sorted_result {
402 let item = sorted_stream.next().await.unwrap().unwrap();
403 assert_eq!(item.0, value.unwrap());
404 assert_eq!(item.1.iter_ones().collect::<Vec<_>>(), offsets);
405 }
406 }
407
408 #[tokio::test]
409 async fn test_external_sorter_pure_in_memory() {
410 let current_memory_usage_threshold = None;
411 let global_memory_usage_sort_limit = None;
412 let total_row_count_cases = vec![0, 100, 1000, 10000];
413 let segment_row_count_cases = vec![1, 10, 100, 1000];
414 let batch_push_cases = vec![false, true];
415
416 for total_row_count in total_row_count_cases {
417 for segment_row_count in &segment_row_count_cases {
418 for batch_push in &batch_push_cases {
419 test_external_sorter(
420 current_memory_usage_threshold,
421 global_memory_usage_sort_limit,
422 *segment_row_count,
423 total_row_count,
424 *batch_push,
425 )
426 .await;
427 }
428 }
429 }
430 }
431
432 #[tokio::test]
433 async fn test_external_sorter_pure_external() {
434 let current_memory_usage_threshold = None;
435 let global_memory_usage_sort_limit = Some(0);
436 let total_row_count_cases = vec![0, 100, 1000, 10000];
437 let segment_row_count_cases = vec![1, 10, 100, 1000];
438 let batch_push_cases = vec![false, true];
439
440 for total_row_count in total_row_count_cases {
441 for segment_row_count in &segment_row_count_cases {
442 for batch_push in &batch_push_cases {
443 test_external_sorter(
444 current_memory_usage_threshold,
445 global_memory_usage_sort_limit,
446 *segment_row_count,
447 total_row_count,
448 *batch_push,
449 )
450 .await;
451 }
452 }
453 }
454 }
455
456 #[tokio::test]
457 async fn test_external_sorter_mixed() {
458 let current_memory_usage_threshold = vec![None, Some(2048)];
459 let global_memory_usage_sort_limit = Some(1024);
460 let total_row_count_cases = vec![0, 100, 1000, 10000];
461 let segment_row_count_cases = vec![1, 10, 100, 1000];
462 let batch_push_cases = vec![false, true];
463
464 for total_row_count in total_row_count_cases {
465 for segment_row_count in &segment_row_count_cases {
466 for batch_push in &batch_push_cases {
467 for current_memory_usage_threshold in ¤t_memory_usage_threshold {
468 test_external_sorter(
469 *current_memory_usage_threshold,
470 global_memory_usage_sort_limit,
471 *segment_row_count,
472 total_row_count,
473 *batch_push,
474 )
475 .await;
476 }
477 }
478 }
479 }
480 }
481
482 fn random_option_bytes(size: usize) -> Option<Vec<u8>> {
483 let mut rng = rand::rng();
484
485 if rng.random() {
486 let mut buffer = vec![0u8; size];
487 rng.fill(&mut buffer[..]);
488 Some(buffer)
489 } else {
490 None
491 }
492 }
493
494 type Values = Vec<Option<Bytes>>;
495 type DictionaryValues = Vec<(Option<Bytes>, usize)>;
496 type ValueSegIds = BTreeMap<Option<Bytes>, Vec<usize>>;
497
498 fn shuffle_values_and_sorted_result(
499 row_count: usize,
500 segment_row_count: usize,
501 ) -> (Values, ValueSegIds) {
502 let mock_values = iter::repeat_with(|| random_option_bytes(100))
503 .take(row_count)
504 .collect::<Vec<_>>();
505
506 let sorted_result = sorted_result(&mock_values, segment_row_count);
507 (mock_values, sorted_result)
508 }
509
510 fn dictionary_values_and_sorted_result(
511 row_count: usize,
512 segment_row_count: usize,
513 ) -> (DictionaryValues, ValueSegIds) {
514 let mut n = row_count;
515 let mut rng = rand::rng();
516 let mut dic_values = Vec::new();
517
518 while n > 0 {
519 let size = rng.random_range(1..=n);
520 let value = random_option_bytes(100);
521 dic_values.push((value, size));
522 n -= size;
523 }
524
525 let mock_values = dic_values
526 .iter()
527 .flat_map(|(value, size)| std::iter::repeat_n(value.clone(), *size))
528 .collect::<Vec<_>>();
529
530 let sorted_result = sorted_result(&mock_values, segment_row_count);
531 (dic_values, sorted_result)
532 }
533
534 fn sorted_result(values: &Values, segment_row_count: usize) -> ValueSegIds {
535 let mut sorted_result = BTreeMap::new();
536 for (row_index, value) in values.iter().enumerate() {
537 let to_add_segment_index = row_index / segment_row_count;
538 let indices = sorted_result.entry(value.clone()).or_insert_with(Vec::new);
539
540 if indices.last() != Some(&to_add_segment_index) {
541 indices.push(to_add_segment_index);
542 }
543 }
544
545 sorted_result
546 }
547}