Integrate facet level bulk update

Only the facet bulk update has been added so far, the incremental must be completely rewritten

Factorize facet merging

Fix facet level extraction
This commit is contained in:
ManyTheFish 2024-10-01 16:13:08 +02:00
parent 774ed28539
commit 14261f8f04
5 changed files with 181 additions and 67 deletions

View File

@ -120,6 +120,7 @@ pub struct WriterOperation {
entry: EntryOperation, entry: EntryOperation,
} }
#[derive(Debug)]
pub enum Database { pub enum Database {
Documents, Documents,
ExternalDocumentsIds, ExternalDocumentsIds,
@ -158,6 +159,18 @@ impl Database {
} }
} }
impl From<FacetKind> for Database {
fn from(value: FacetKind) -> Self {
match value {
FacetKind::Number => Database::FacetIdF64NumberDocids,
FacetKind::String => Database::FacetIdStringDocids,
FacetKind::Null => Database::FacetIdIsNullDocids,
FacetKind::Empty => Database::FacetIdIsEmptyDocids,
FacetKind::Exists => Database::FacetIdExistsDocids,
}
}
}
impl WriterOperation { impl WriterOperation {
pub fn database(&self, index: &Index) -> heed::Database<Bytes, Bytes> { pub fn database(&self, index: &Index) -> heed::Database<Bytes, Bytes> {
self.database.database(index) self.database.database(index)
@ -395,8 +408,18 @@ pub struct FacetDocidsSender<'a> {
impl DocidsSender for FacetDocidsSender<'_> { impl DocidsSender for FacetDocidsSender<'_> {
fn write(&self, key: &[u8], value: &[u8]) -> StdResult<(), SendError<()>> { fn write(&self, key: &[u8], value: &[u8]) -> StdResult<(), SendError<()>> {
let (database, key) = self.extract_database(key); let (facet_kind, key) = FacetKind::extract_from_key(key);
let entry = EntryOperation::Write(KeyValueEntry::from_small_key_value(key, value)); let database = Database::from(facet_kind);
// let entry = EntryOperation::Write(KeyValueEntry::from_small_key_value(key, value));
let entry = match facet_kind {
// skip level group size
FacetKind::String | FacetKind::Number => {
// add facet group size
let value = [&[1], value].concat();
EntryOperation::Write(KeyValueEntry::from_small_key_value(key, &value))
}
_ => EntryOperation::Write(KeyValueEntry::from_small_key_value(key, value)),
};
match self.sender.send(WriterOperation { database, entry }) { match self.sender.send(WriterOperation { database, entry }) {
Ok(()) => Ok(()), Ok(()) => Ok(()),
Err(SendError(_)) => Err(SendError(())), Err(SendError(_)) => Err(SendError(())),
@ -404,7 +427,8 @@ impl DocidsSender for FacetDocidsSender<'_> {
} }
fn delete(&self, key: &[u8]) -> StdResult<(), SendError<()>> { fn delete(&self, key: &[u8]) -> StdResult<(), SendError<()>> {
let (database, key) = self.extract_database(key); let (facet_kind, key) = FacetKind::extract_from_key(key);
let database = Database::from(facet_kind);
let entry = EntryOperation::Delete(KeyEntry::from_key(key)); let entry = EntryOperation::Delete(KeyEntry::from_key(key));
match self.sender.send(WriterOperation { database, entry }) { match self.sender.send(WriterOperation { database, entry }) {
Ok(()) => Ok(()), Ok(()) => Ok(()),
@ -413,19 +437,6 @@ impl DocidsSender for FacetDocidsSender<'_> {
} }
} }
impl FacetDocidsSender<'_> {
fn extract_database<'a>(&self, key: &'a [u8]) -> (Database, &'a [u8]) {
let database = match FacetKind::from(key[0]) {
FacetKind::Number => Database::FacetIdF64NumberDocids,
FacetKind::String => Database::FacetIdStringDocids,
FacetKind::Null => Database::FacetIdIsNullDocids,
FacetKind::Empty => Database::FacetIdIsEmptyDocids,
FacetKind::Exists => Database::FacetIdExistsDocids,
};
(database, &key[1..])
}
}
pub struct DocumentsSender<'a>(&'a MergerSender); pub struct DocumentsSender<'a>(&'a MergerSender);
impl DocumentsSender<'_> { impl DocumentsSender<'_> {

View File

@ -129,7 +129,7 @@ impl FacetedDocidsExtractor {
buffer.clear(); buffer.clear();
buffer.push(FacetKind::Number as u8); buffer.push(FacetKind::Number as u8);
buffer.extend_from_slice(&fid.to_be_bytes()); buffer.extend_from_slice(&fid.to_be_bytes());
buffer.push(1); // level 0 buffer.push(0); // level 0
buffer.extend_from_slice(&ordered); buffer.extend_from_slice(&ordered);
buffer.extend_from_slice(&n.to_be_bytes()); buffer.extend_from_slice(&n.to_be_bytes());
@ -145,7 +145,7 @@ impl FacetedDocidsExtractor {
buffer.clear(); buffer.clear();
buffer.push(FacetKind::String as u8); buffer.push(FacetKind::String as u8);
buffer.extend_from_slice(&fid.to_be_bytes()); buffer.extend_from_slice(&fid.to_be_bytes());
buffer.push(1); // level 0 buffer.push(0); // level 0
buffer.extend_from_slice(truncated.as_bytes()); buffer.extend_from_slice(truncated.as_bytes());
cache_fn(cached_sorter, &*buffer, docid).map_err(Into::into) cache_fn(cached_sorter, &*buffer, docid).map_err(Into::into)
} }

View File

@ -4,6 +4,7 @@ mod facet_document;
pub use extract_facets::FacetedDocidsExtractor; pub use extract_facets::FacetedDocidsExtractor;
#[repr(u8)] #[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub enum FacetKind { pub enum FacetKind {
Number = 0, Number = 0,
String = 1, String = 1,
@ -24,3 +25,10 @@ impl From<u8> for FacetKind {
} }
} }
} }
impl FacetKind {
pub fn extract_from_key<'k>(key: &'k [u8]) -> (FacetKind, &'k [u8]) {
debug_assert!(key.len() > 3);
(FacetKind::from(key[0]), &key[1..])
}
}

View File

@ -13,18 +13,19 @@ pub use update_by_function::UpdateByFunction;
use super::channel::*; use super::channel::*;
use super::document_change::{Deletion, DocumentChange, Insertion, Update}; use super::document_change::{Deletion, DocumentChange, Insertion, Update};
use super::extract::*; use super::extract::*;
use super::merger::merge_grenad_entries; use super::merger::{merge_grenad_entries, FacetFieldIdsDelta};
use super::word_fst_builder::PrefixDelta; use super::word_fst_builder::PrefixDelta;
use super::words_prefix_docids::{ use super::words_prefix_docids::{
compute_word_prefix_docids, compute_word_prefix_fid_docids, compute_word_prefix_position_docids, compute_word_prefix_docids, compute_word_prefix_fid_docids, compute_word_prefix_position_docids,
}; };
use super::{StdResult, TopLevelMap}; use super::{StdResult, TopLevelMap};
use crate::documents::{PrimaryKey, DEFAULT_PRIMARY_KEY}; use crate::documents::{PrimaryKey, DEFAULT_PRIMARY_KEY};
use crate::facet::FacetType;
use crate::update::new::channel::ExtractorSender; use crate::update::new::channel::ExtractorSender;
use crate::update::settings::InnerIndexSettings; use crate::update::settings::InnerIndexSettings;
use crate::update::new::parallel_iterator_ext::ParallelIteratorExt; use crate::update::new::parallel_iterator_ext::ParallelIteratorExt;
use crate::update::GrenadParameters;
use crate::{Error, FieldsIdsMap, GlobalFieldsIdsMap, Index, Result, UserError}; use crate::{Error, FieldsIdsMap, GlobalFieldsIdsMap, Index, Result, UserError};
use crate::update::{FacetsUpdateBulk, GrenadParameters};
mod document_deletion; mod document_deletion;
mod document_operation; mod document_operation;
@ -71,11 +72,11 @@ where
let global_fields_ids_map_clone = global_fields_ids_map.clone(); let global_fields_ids_map_clone = global_fields_ids_map.clone();
thread::scope(|s| { thread::scope(|s| {
let indexer_span = tracing::Span::current();
// TODO manage the errors correctly // TODO manage the errors correctly
let current_span = tracing::Span::current();
let handle = Builder::new().name(S("indexer-extractors")).spawn_scoped(s, move || { let handle = Builder::new().name(S("indexer-extractors")).spawn_scoped(s, move || {
pool.in_place_scope(|_s| { pool.in_place_scope(|_s| {
let span = tracing::trace_span!(target: "indexing::documents", parent: &current_span, "extract"); let span = tracing::trace_span!(target: "indexing::documents", parent: &indexer_span, "extract");
let _entered = span.enter(); let _entered = span.enter();
let document_changes = document_changes.into_par_iter(); let document_changes = document_changes.into_par_iter();
@ -179,11 +180,11 @@ where
}) })
})?; })?;
let indexer_span = tracing::Span::current();
// TODO manage the errors correctly // TODO manage the errors correctly
let current_span = tracing::Span::current();
let merger_thread = Builder::new().name(S("indexer-merger")).spawn_scoped(s, move || { let merger_thread = Builder::new().name(S("indexer-merger")).spawn_scoped(s, move || {
let span = let span =
tracing::trace_span!(target: "indexing::documents", parent: &current_span, "merge"); tracing::trace_span!(target: "indexing::documents", parent: &indexer_span, "merge");
let _entered = span.enter(); let _entered = span.enter();
let rtxn = index.read_txn().unwrap(); let rtxn = index.read_txn().unwrap();
merge_grenad_entries( merge_grenad_entries(
@ -211,17 +212,12 @@ where
handle.join().unwrap()?; handle.join().unwrap()?;
let merger_result = merger_thread.join().unwrap()?; let merger_result = merger_thread.join().unwrap()?;
if let Some(prefix_delta) = merger_result.prefix_delta { if let Some(facet_field_ids_delta) = merger_result.facet_field_ids_delta {
let span = tracing::trace_span!(target: "indexing", "prefix"); compute_facet_level_database(index, wtxn, facet_field_ids_delta)?;
let _entered = span.enter(); }
let PrefixDelta { modified, deleted } = prefix_delta; if let Some(prefix_delta) = merger_result.prefix_delta {
// Compute word prefix docids compute_prefix_database(index, wtxn, prefix_delta)?;
compute_word_prefix_docids(wtxn, index, &modified, &deleted)?;
// Compute word prefix fid docids
compute_word_prefix_fid_docids(wtxn, index, &modified, &deleted)?;
// Compute word prefix position docids
compute_word_prefix_position_docids(wtxn, index, &modified, &deleted)?;
} }
Ok(()) as Result<_> Ok(()) as Result<_>
@ -238,6 +234,51 @@ where
Ok(()) Ok(())
} }
#[tracing::instrument(level = "trace", skip_all, target = "indexing::prefix")]
fn compute_prefix_database(
index: &Index,
wtxn: &mut RwTxn,
prefix_delta: PrefixDelta,
) -> Result<()> {
let PrefixDelta { modified, deleted } = prefix_delta;
// Compute word prefix docids
compute_word_prefix_docids(wtxn, index, &modified, &deleted)?;
// Compute word prefix fid docids
compute_word_prefix_fid_docids(wtxn, index, &modified, &deleted)?;
// Compute word prefix position docids
compute_word_prefix_position_docids(wtxn, index, &modified, &deleted)
}
#[tracing::instrument(level = "trace", skip_all, target = "indexing::facet_field_ids")]
fn compute_facet_level_database(
index: &Index,
wtxn: &mut RwTxn,
facet_field_ids_delta: FacetFieldIdsDelta,
) -> Result<()> {
if let Some(modified_facet_string_ids) = facet_field_ids_delta.modified_facet_string_ids() {
let span = tracing::trace_span!(target: "indexing::facet_field_ids", "string");
let _entered = span.enter();
FacetsUpdateBulk::new_not_updating_level_0(
index,
modified_facet_string_ids,
FacetType::String,
)
.execute(wtxn)?;
}
if let Some(modified_facet_number_ids) = facet_field_ids_delta.modified_facet_number_ids() {
let span = tracing::trace_span!(target: "indexing::facet_field_ids", "number");
let _entered = span.enter();
FacetsUpdateBulk::new_not_updating_level_0(
index,
modified_facet_number_ids,
FacetType::Number,
)
.execute(wtxn)?;
}
Ok(())
}
/// TODO: GrenadParameters::default() should be removed in favor a passed parameter /// TODO: GrenadParameters::default() should be removed in favor a passed parameter
/// TODO: manage the errors correctly /// TODO: manage the errors correctly
/// TODO: we must have a single trait that also gives the extractor type /// TODO: we must have a single trait that also gives the extractor type

View File

@ -16,7 +16,9 @@ use crate::update::del_add::DelAdd;
use crate::update::new::channel::MergerOperation; use crate::update::new::channel::MergerOperation;
use crate::update::new::word_fst_builder::WordFstBuilder; use crate::update::new::word_fst_builder::WordFstBuilder;
use crate::update::MergeDeladdCboRoaringBitmaps; use crate::update::MergeDeladdCboRoaringBitmaps;
use crate::{CboRoaringBitmapCodec, Error, GeoPoint, GlobalFieldsIdsMap, Index, Prefix, Result}; use crate::{
CboRoaringBitmapCodec, Error, FieldId, GeoPoint, GlobalFieldsIdsMap, Index, Prefix, Result,
};
/// TODO We must return some infos/stats /// TODO We must return some infos/stats
#[tracing::instrument(level = "trace", skip_all, target = "indexing::documents", name = "merge")] #[tracing::instrument(level = "trace", skip_all, target = "indexing::documents", name = "merge")]
@ -188,13 +190,17 @@ pub fn merge_grenad_entries(
let span = let span =
tracing::trace_span!(target: "indexing::documents::merge", "facet_docids"); tracing::trace_span!(target: "indexing::documents::merge", "facet_docids");
let _entered = span.enter(); let _entered = span.enter();
let mut facet_field_ids_delta = FacetFieldIdsDelta::new();
merge_and_send_facet_docids( merge_and_send_facet_docids(
merger, merger,
FacetDatabases::new(index), FacetDatabases::new(index),
rtxn, rtxn,
&mut buffer, &mut buffer,
sender.facet_docids(), sender.facet_docids(),
&mut facet_field_ids_delta,
)?; )?;
merger_result.facet_field_ids_delta = Some(facet_field_ids_delta);
} }
} }
} }
@ -218,6 +224,8 @@ pub fn merge_grenad_entries(
pub struct MergerResult { pub struct MergerResult {
/// The delta of the prefixes /// The delta of the prefixes
pub prefix_delta: Option<PrefixDelta>, pub prefix_delta: Option<PrefixDelta>,
/// The field ids that have been modified
pub facet_field_ids_delta: Option<FacetFieldIdsDelta>,
} }
pub struct GeoExtractor { pub struct GeoExtractor {
@ -308,20 +316,23 @@ fn merge_and_send_facet_docids(
rtxn: &RoTxn<'_>, rtxn: &RoTxn<'_>,
buffer: &mut Vec<u8>, buffer: &mut Vec<u8>,
docids_sender: impl DocidsSender, docids_sender: impl DocidsSender,
facet_field_ids_delta: &mut FacetFieldIdsDelta,
) -> Result<()> { ) -> Result<()> {
let mut merger_iter = merger.into_stream_merger_iter().unwrap(); let mut merger_iter = merger.into_stream_merger_iter().unwrap();
while let Some((key, deladd)) = merger_iter.next().unwrap() { while let Some((key, deladd)) = merger_iter.next().unwrap() {
let current = database.get(rtxn, key)?; let current = database.get_cbo_roaring_bytes_value(rtxn, key)?;
let deladd: &KvReaderDelAdd = deladd.into(); let deladd: &KvReaderDelAdd = deladd.into();
let del = deladd.get(DelAdd::Deletion); let del = deladd.get(DelAdd::Deletion);
let add = deladd.get(DelAdd::Addition); let add = deladd.get(DelAdd::Addition);
match merge_cbo_bitmaps(current, del, add)? { match merge_cbo_bitmaps(current, del, add)? {
Operation::Write(bitmap) => { Operation::Write(bitmap) => {
facet_field_ids_delta.register_from_key(key);
let value = cbo_bitmap_serialize_into_vec(&bitmap, buffer); let value = cbo_bitmap_serialize_into_vec(&bitmap, buffer);
docids_sender.write(key, value).unwrap(); docids_sender.write(key, value).unwrap();
} }
Operation::Delete => { Operation::Delete => {
facet_field_ids_delta.register_from_key(key);
docids_sender.delete(key).unwrap(); docids_sender.delete(key).unwrap();
} }
Operation::Ignore => (), Operation::Ignore => (),
@ -331,43 +342,84 @@ fn merge_and_send_facet_docids(
Ok(()) Ok(())
} }
struct FacetDatabases { struct FacetDatabases<'a> {
/// Maps the facet field id and the docids for which this field exists index: &'a Index,
facet_id_exists_docids: Database<Bytes, Bytes>,
/// Maps the facet field id and the docids for which this field is set as null
facet_id_is_null_docids: Database<Bytes, Bytes>,
/// Maps the facet field id and the docids for which this field is considered empty
facet_id_is_empty_docids: Database<Bytes, Bytes>,
/// Maps the facet field id and ranges of numbers with the docids that corresponds to them.
facet_id_f64_docids: Database<Bytes, Bytes>,
/// Maps the facet field id and ranges of strings with the docids that corresponds to them.
facet_id_string_docids: Database<Bytes, Bytes>,
} }
impl FacetDatabases { impl<'a> FacetDatabases<'a> {
fn new(index: &Index) -> Self { fn new(index: &'a Index) -> Self {
Self { Self { index }
facet_id_exists_docids: index.facet_id_exists_docids.remap_types(),
facet_id_is_null_docids: index.facet_id_is_null_docids.remap_types(),
facet_id_is_empty_docids: index.facet_id_is_empty_docids.remap_types(),
facet_id_f64_docids: index.facet_id_f64_docids.remap_types(),
facet_id_string_docids: index.facet_id_string_docids.remap_types(),
}
} }
fn get<'a>(&self, rtxn: &'a RoTxn<'_>, key: &[u8]) -> heed::Result<Option<&'a [u8]>> { fn get_cbo_roaring_bytes_value<'t>(
let (facet_kind, key) = self.extract_facet_kind(key); &self,
rtxn: &'t RoTxn<'_>,
key: &[u8],
) -> heed::Result<Option<&'t [u8]>> {
let (facet_kind, key) = FacetKind::extract_from_key(key);
let value =
super::channel::Database::from(facet_kind).database(self.index).get(rtxn, key)?;
match facet_kind { match facet_kind {
FacetKind::Exists => self.facet_id_exists_docids.get(rtxn, key), // skip level group size
FacetKind::Null => self.facet_id_is_null_docids.get(rtxn, key), FacetKind::String | FacetKind::Number => Ok(value.map(|v| &v[1..])),
FacetKind::Empty => self.facet_id_is_empty_docids.get(rtxn, key), _ => Ok(value),
FacetKind::Number => self.facet_id_f64_docids.get(rtxn, key), }
FacetKind::String => self.facet_id_string_docids.get(rtxn, key), }
}
#[derive(Debug)]
pub struct FacetFieldIdsDelta {
/// The field ids that have been modified
modified_facet_string_ids: HashSet<FieldId>,
modified_facet_number_ids: HashSet<FieldId>,
}
impl FacetFieldIdsDelta {
fn new() -> Self {
Self {
modified_facet_string_ids: HashSet::new(),
modified_facet_number_ids: HashSet::new(),
} }
} }
fn extract_facet_kind<'a>(&self, key: &'a [u8]) -> (FacetKind, &'a [u8]) { fn register_facet_string_id(&mut self, field_id: FieldId) {
(FacetKind::from(key[0]), &key[1..]) self.modified_facet_string_ids.insert(field_id);
}
fn register_facet_number_id(&mut self, field_id: FieldId) {
self.modified_facet_number_ids.insert(field_id);
}
fn register_from_key(&mut self, key: &[u8]) {
let (facet_kind, field_id) = self.extract_key_data(key);
match facet_kind {
FacetKind::Number => self.register_facet_number_id(field_id),
FacetKind::String => self.register_facet_string_id(field_id),
_ => (),
}
}
fn extract_key_data<'a>(&self, key: &'a [u8]) -> (FacetKind, FieldId) {
let facet_kind = FacetKind::from(key[0]);
let field_id = FieldId::from_be_bytes([key[1], key[2]]);
(facet_kind, field_id)
}
pub fn modified_facet_string_ids(&self) -> Option<Vec<FieldId>> {
if self.modified_facet_string_ids.is_empty() {
None
} else {
Some(self.modified_facet_string_ids.iter().copied().collect())
}
}
pub fn modified_facet_number_ids(&self) -> Option<Vec<FieldId>> {
if self.modified_facet_number_ids.is_empty() {
None
} else {
Some(self.modified_facet_number_ids.iter().copied().collect())
}
} }
} }
@ -396,11 +448,13 @@ fn merge_cbo_bitmaps(
(Some(current), None, Some(add)) => Ok(Operation::Write(current | add)), (Some(current), None, Some(add)) => Ok(Operation::Write(current | add)),
(Some(current), Some(del), add) => { (Some(current), Some(del), add) => {
let output = match add { let output = match add {
Some(add) => (current - del) | add, Some(add) => (&current - del) | add,
None => current - del, None => &current - del,
}; };
if output.is_empty() { if output.is_empty() {
Ok(Operation::Delete) Ok(Operation::Delete)
} else if current == output {
Ok(Operation::Ignore)
} else { } else {
Ok(Operation::Write(output)) Ok(Operation::Write(output))
} }