JMAP Registry API implementation - part 4

This commit is contained in:
mdecimus
2026-02-26 18:11:41 +01:00
parent f16e737221
commit e24b595a16
44 changed files with 1562 additions and 1718 deletions

View File

@@ -27,15 +27,13 @@ use registry::{
Role, SubAddressing, Tenant,
},
},
types::{
id::ObjectId,
index::{IndexKey, IndexValue},
},
types::{EnumImpl, id::ObjectId},
};
use std::{borrow::Cow, sync::Arc};
use store::{
U64_LEN, ValueKey,
registry::{RegistryQuery, bootstrap::Bootstrap},
write::{RegistryClass, now},
write::{RegistryClass, ValueClass, key::KeySerializer, now},
};
use trc::AddContext;
use types::id::Id;
@@ -161,19 +159,20 @@ impl Server {
.get(&EmailAddressRef::new(local_part, domain_id))
.is_none()
{
let key = IndexKey::Global {
property: Property::Email,
value_1: IndexValue::Text(local_part.into()),
value_2: IndexValue::U64(domain_id.into()),
};
let key = ValueKey::from(ValueClass::Registry(RegistryClass::PrimaryKey {
object_id: None,
index_id: Property::Email.to_id(),
key: KeySerializer::new(local_part.len() + U64_LEN)
.write(local_part.as_bytes())
.write(domain_id as u64)
.finalize(),
}));
if let Some(object) = self
.registry()
.validate_primary_key(
RegistryClass::from_index_key(&key, 0, 0),
RegistryClass::from_index_key(&key, u16::MAX, u64::MAX),
None,
)
.await?
.store()
.get_value::<ObjectId>(key)
.await
.caused_by(trc::location!())?
{
let item_id = object.id().document_id();
let result = match object.object() {

View File

@@ -508,7 +508,7 @@ impl Tracers {
.copied()
.unwrap_or(event_type.level());
if Level::Info.is_contained(event_level) {
global_interests.set(event_type);
global_interests.set(event_type.to_id() as usize);
}
}

View File

@@ -154,6 +154,7 @@ pub struct DmarcEvent {
pub report_record: Record,
pub dmarc_record: Arc<Dmarc>,
pub interval: AggregateFrequency,
pub span_id: u64,
}
#[derive(Debug)]
@@ -163,6 +164,7 @@ pub struct TlsEvent {
pub failure: Option<FailureDetails>,
pub tls_record: Arc<TlsRpt>,
pub interval: AggregateFrequency,
pub span_id: u64,
}
#[derive(Debug, Hash, PartialEq, Eq)]

View File

@@ -316,7 +316,7 @@ impl Family {
Family::Registry => &[
SUBSPACE_REGISTRY,
SUBSPACE_REGISTRY_IDX,
SUBSPACE_REGISTRY_IDX_GLOBAL,
SUBSPACE_REGISTRY_PK,
SUBSPACE_DIRECTORY,
],
Family::Changelog => &[SUBSPACE_LOGS],

View File

@@ -9,10 +9,7 @@ use base64::engine::general_purpose;
use std::env;
use std::io::{self, Write};
use store::write::{AnyClass, AnyKey, BatchBuilder, ValueClass};
use store::{
Deserialize, IterateParams, SUBSPACE_INDEXES, SUBSPACE_REGISTRY_IDX,
SUBSPACE_REGISTRY_IDX_GLOBAL, Store,
};
use store::{Deserialize, IterateParams, SUBSPACE_INDEXES, SUBSPACE_REGISTRY_IDX, Store};
const HELP: &str = concat!(
"Stalwart Server v",
@@ -76,12 +73,7 @@ pub async fn store_console(store: Store) {
},
)
.set_values(
![
SUBSPACE_INDEXES,
SUBSPACE_REGISTRY_IDX,
SUBSPACE_REGISTRY_IDX_GLOBAL,
]
.contains(&from_subspace),
![SUBSPACE_INDEXES, SUBSPACE_REGISTRY_IDX].contains(&from_subspace),
),
|key, value| {
print!("{}", char::from(from_subspace));

View File

@@ -15,7 +15,7 @@ use std::{
};
use store::{
BlobStore, SUBSPACE_BLOBS, SUBSPACE_COUNTER, SUBSPACE_INDEXES, SUBSPACE_QUOTA,
SUBSPACE_REGISTRY_IDX, SUBSPACE_REGISTRY_IDX_GLOBAL, Store, U32_LEN,
SUBSPACE_REGISTRY_IDX, Store, U32_LEN,
write::{AnyClass, BatchBuilder, ValueClass, key::DeserializeBigEndian},
};
use types::{collection::Collection, field::Field};
@@ -85,7 +85,7 @@ async fn restore_file(store: Store, blob_store: BlobStore, path: &Path) {
}
}
}
SUBSPACE_INDEXES | SUBSPACE_REGISTRY_IDX | SUBSPACE_REGISTRY_IDX_GLOBAL => {
SUBSPACE_INDEXES | SUBSPACE_REGISTRY_IDX => {
while let Some((key, _)) = reader.next() {
let account_id = key
.as_slice()

View File

@@ -8,40 +8,32 @@
*
*/
use crate::Core;
use ahash::AHashMap;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use registry::{
pickle::Pickle,
schema::structs::{Metric, MetricCount, MetricSum},
};
use std::{future::Future, sync::Arc, time::Duration};
use store::{
IterateParams, Store, U32_LEN, U64_LEN, ValueKey,
write::{
BatchBuilder, TelemetryClass, ValueClass,
key::{DeserializeBigEndian, KeySerializer},
now,
},
Store, ValueKey,
write::{BatchBuilder, TelemetryClass, ValueClass},
};
use trc::*;
use utils::codec::leb128::Leb128Reader;
use utils::snowflake::SnowflakeIdGenerator;
pub trait MetricsStore: Sync + Send {
fn write_metrics(
&self,
core: Arc<Core>,
timestamp: u64,
timestamp: Option<u64>,
history: SharedMetricHistory,
) -> impl Future<Output = trc::Result<()>> + Send;
fn query_metrics(
&self,
from_timestamp: u64,
to_timestamp: u64,
) -> impl Future<Output = trc::Result<Vec<Metric<MetricType, MetricType, u64>>>> + Send;
fn purge_metrics(&self, period: Duration) -> impl Future<Output = trc::Result<()>> + Send;
}
#[derive(Default)]
pub struct MetricsHistory {
events: AHashMap<EventType, u32>,
events: AHashMap<MetricType, u32>,
histograms: AHashMap<MetricType, HistogramHistory>,
}
@@ -51,81 +43,71 @@ struct HistogramHistory {
count: u64,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type")]
#[serde(rename_all = "camelCase")]
pub enum Metric<CI, MI, T> {
Counter {
id: CI,
timestamp: T,
value: u64,
},
Gauge {
id: MI,
timestamp: T,
value: u64,
},
Histogram {
id: MI,
timestamp: T,
count: u64,
sum: u64,
},
}
pub type SharedMetricHistory = Arc<Mutex<MetricsHistory>>;
const TYPE_COUNTER: u64 = 0x00;
const TYPE_HISTOGRAM: u64 = 0x01;
const TYPE_GAUGE: u64 = 0x02;
impl MetricsStore for Store {
async fn write_metrics(
&self,
core: Arc<Core>,
timestamp: u64,
_timestamp: Option<u64>,
history_: SharedMetricHistory,
) -> trc::Result<()> {
let mut batch = BatchBuilder::new();
{
let node_id = core.network.node_id;
let mut history = history_.lock();
for event in [
EventType::Smtp(SmtpEvent::ConnectionStart),
EventType::Imap(ImapEvent::ConnectionStart),
EventType::Pop3(Pop3Event::ConnectionStart),
EventType::ManageSieve(ManageSieveEvent::ConnectionStart),
EventType::Http(HttpEvent::ConnectionStart),
EventType::Delivery(DeliveryEvent::AttemptStart),
EventType::Queue(QueueEvent::QueueMessage),
EventType::Queue(QueueEvent::QueueMessageAuthenticated),
EventType::Queue(QueueEvent::QueueDsn),
EventType::Queue(QueueEvent::QueueReport),
EventType::MessageIngest(MessageIngestEvent::Ham),
EventType::MessageIngest(MessageIngestEvent::Spam),
EventType::Auth(AuthEvent::Failed),
EventType::Security(SecurityEvent::AuthenticationBan),
EventType::Security(SecurityEvent::ScanBan),
EventType::Security(SecurityEvent::AbuseBan),
EventType::Security(SecurityEvent::LoiterBan),
EventType::Security(SecurityEvent::IpBlocked),
EventType::IncomingReport(IncomingReportEvent::DmarcReport),
EventType::IncomingReport(IncomingReportEvent::DmarcReportWithWarnings),
EventType::IncomingReport(IncomingReportEvent::TlsReport),
EventType::IncomingReport(IncomingReportEvent::TlsReportWithWarnings),
MetricType::SmtpConnectionStart,
MetricType::ImapConnectionStart,
MetricType::Pop3ConnectionStart,
MetricType::ManageSieveConnectionStart,
MetricType::HttpConnectionStart,
MetricType::DeliveryAttemptStart,
MetricType::QueueQueueMessage,
MetricType::QueueQueueMessageAuthenticated,
MetricType::QueueQueueDsn,
MetricType::QueueQueueReport,
MetricType::MessageIngestHam,
MetricType::MessageIngestSpam,
MetricType::AuthFailed,
MetricType::SecurityAuthenticationBan,
MetricType::SecurityScanBan,
MetricType::SecurityAbuseBan,
MetricType::SecurityLoiterBan,
MetricType::SecurityIpBlocked,
MetricType::IncomingReportDmarcReport,
MetricType::IncomingReportDmarcReportWithWarnings,
MetricType::IncomingReportTlsReport,
MetricType::IncomingReportTlsReportWithWarnings,
] {
let reading = Collector::read_metric_counter(event.to_id() as usize);
let reading = Collector::read_metric_counter(event.event_id());
if reading > 0 {
let history = history.events.entry(event).or_insert(0);
let diff = reading - *history;
#[cfg(not(feature = "test_mode"))]
let metric_id = SnowflakeIdGenerator::from_sequence_id(event.to_id() as u64)
.unwrap_or_default();
#[cfg(feature = "test_mode")]
let metric_id = _timestamp
.map(|timestamp| {
SnowflakeIdGenerator::from_timestamp_and_sequence_id(
timestamp,
event.to_id() as u64,
)
})
.unwrap_or_else(|| {
SnowflakeIdGenerator::from_sequence_id(event.to_id() as u64)
})
.unwrap_or_default();
if diff > 0 {
batch.set(
ValueClass::Telemetry(TelemetryClass::Metric {
timestamp,
metric_id: (event.to_id() << 2) as u64 | TYPE_COUNTER,
node_id,
}),
KeySerializer::new(U32_LEN).write_leb128(diff).finalize(),
ValueClass::Telemetry(TelemetryClass::Metric(metric_id)),
Metric::Counter(MetricCount {
count: diff as u64,
metric: event,
})
.to_pickled_vec(),
);
}
*history = reading;
@@ -133,48 +115,77 @@ impl MetricsStore for Store {
}
for gauge in Collector::collect_gauges(true) {
let gauge_id = gauge.id();
if matches!(gauge_id, MetricType::QueueCount | MetricType::ServerMemory) {
let metric = gauge.id();
if matches!(metric, MetricType::QueueCount | MetricType::ServerMemory) {
let value = gauge.get();
if value > 0 {
#[cfg(not(feature = "test_mode"))]
let metric_id =
SnowflakeIdGenerator::from_sequence_id(metric.to_id() as u64)
.unwrap_or_default();
#[cfg(feature = "test_mode")]
let metric_id = _timestamp
.map(|timestamp| {
SnowflakeIdGenerator::from_timestamp_and_sequence_id(
timestamp,
metric.to_id() as u64,
)
})
.unwrap_or_else(|| {
SnowflakeIdGenerator::from_sequence_id(metric.to_id() as u64)
})
.unwrap_or_default();
batch.set(
ValueClass::Telemetry(TelemetryClass::Metric {
timestamp,
metric_id: (gauge_id.to_id() << 2) as u64 | TYPE_GAUGE,
node_id,
}),
KeySerializer::new(U32_LEN).write_leb128(value).finalize(),
ValueClass::Telemetry(TelemetryClass::Metric(metric_id)),
Metric::Gauge(MetricCount {
count: value,
metric,
})
.to_pickled_vec(),
);
}
}
}
for histogram in Collector::collect_histograms(true) {
let histogram_id = histogram.id();
let metric = histogram.id();
if matches!(
histogram_id,
metric,
MetricType::MessageIngestTime
| MetricType::MessageIngestIndexTime
| MetricType::DeliveryTotalTime
| MetricType::DeliveryAttemptTime
| MetricType::DnsLookupTime
) {
let history = history.histograms.entry(histogram_id).or_default();
let history = history.histograms.entry(metric).or_default();
let sum = histogram.sum();
let count = histogram.count();
let diff_sum = sum - history.sum;
let diff_count = count - history.count;
if diff_sum > 0 || diff_count > 0 {
#[cfg(not(feature = "test_mode"))]
let metric_id =
SnowflakeIdGenerator::from_sequence_id(metric.to_id() as u64)
.unwrap_or_default();
#[cfg(feature = "test_mode")]
let metric_id = _timestamp
.map(|timestamp| {
SnowflakeIdGenerator::from_timestamp_and_sequence_id(
timestamp,
metric.to_id() as u64,
)
})
.unwrap_or_else(|| {
SnowflakeIdGenerator::from_sequence_id(metric.to_id() as u64)
})
.unwrap_or_default();
batch.set(
ValueClass::Telemetry(TelemetryClass::Metric {
timestamp,
metric_id: (histogram_id.to_id() << 2) as u64 | TYPE_HISTOGRAM,
node_id,
}),
KeySerializer::new(U32_LEN)
.write_leb128(diff_count)
.write_leb128(diff_sum)
.finalize(),
ValueClass::Telemetry(TelemetryClass::Metric(metric_id)),
Metric::Histogram(MetricSum { count, metric, sum }).to_pickled_vec(),
);
}
history.sum = sum;
@@ -192,105 +203,16 @@ impl MetricsStore for Store {
Ok(())
}
async fn query_metrics(
&self,
from_timestamp: u64,
to_timestamp: u64,
) -> trc::Result<Vec<Metric<MetricType, MetricType, u64>>> {
let mut metrics = Vec::new();
self.iterate(
IterateParams::new(
ValueKey::from(ValueClass::Telemetry(TelemetryClass::Metric {
timestamp: from_timestamp,
metric_id: 0,
node_id: 0,
})),
ValueKey::from(ValueClass::Telemetry(TelemetryClass::Metric {
timestamp: to_timestamp,
metric_id: 0,
node_id: 0,
})),
),
|key, value| {
let timestamp = key.deserialize_be_u64(0).caused_by(trc::location!())?;
let (metric_type, _) = key
.get(U64_LEN..)
.and_then(|bytes| bytes.read_leb128::<u64>())
.ok_or_else(|| trc::Error::corrupted_key(key, None, trc::location!()))?;
match metric_type & 0x03 {
TYPE_COUNTER => {
let id =
MetricType::from_id((metric_type >> 2) as u16).ok_or_else(|| {
trc::Error::corrupted_key(key, None, trc::location!())
})?;
let (value, _) = value.read_leb128::<u64>().ok_or_else(|| {
trc::Error::corrupted_key(key, value.into(), trc::location!())
})?;
metrics.push(Metric::Counter {
id,
timestamp,
value,
});
}
TYPE_HISTOGRAM => {
let id =
MetricType::from_id((metric_type >> 2) as u16).ok_or_else(|| {
trc::Error::corrupted_key(key, None, trc::location!())
})?;
let (count, bytes_read) = value.read_leb128::<u64>().ok_or_else(|| {
trc::Error::corrupted_key(key, value.into(), trc::location!())
})?;
let (sum, _) = value
.get(bytes_read..)
.and_then(|bytes| bytes.read_leb128::<u64>())
.ok_or_else(|| {
trc::Error::corrupted_key(key, value.into(), trc::location!())
})?;
metrics.push(Metric::Histogram {
id,
timestamp,
count,
sum,
});
}
TYPE_GAUGE => {
let id =
MetricType::from_id((metric_type >> 2) as u16).ok_or_else(|| {
trc::Error::corrupted_key(key, None, trc::location!())
})?;
let (value, _) = value.read_leb128::<u64>().ok_or_else(|| {
trc::Error::corrupted_key(key, value.into(), trc::location!())
})?;
metrics.push(Metric::Gauge {
id,
timestamp,
value,
});
}
_ => return Err(trc::Error::corrupted_key(key, None, trc::location!())),
}
Ok(true)
},
)
.await
.caused_by(trc::location!())?;
Ok(metrics)
}
async fn purge_metrics(&self, period: Duration) -> trc::Result<()> {
let until_span_id = SnowflakeIdGenerator::from_duration(period).ok_or_else(|| {
trc::StoreEvent::UnexpectedError
.caused_by(trc::location!())
.ctx(trc::Key::Reason, "Failed to generate reference metric id.")
})?;
self.delete_range(
ValueKey::from(ValueClass::Telemetry(TelemetryClass::Metric {
timestamp: 0,
metric_id: 0,
node_id: 0,
})),
ValueKey::from(ValueClass::Telemetry(TelemetryClass::Metric {
timestamp: now() - period.as_secs(),
metric_id: 0,
node_id: 0,
})),
ValueKey::from(ValueClass::Telemetry(TelemetryClass::Metric(0))),
ValueKey::from(ValueClass::Telemetry(TelemetryClass::Metric(until_span_id))),
)
.await
.caused_by(trc::location!())

View File

@@ -10,18 +10,25 @@
use crate::config::telemetry::StoreTracer;
use ahash::{AHashMap, AHashSet};
use registry::schema::structs::{Task, TaskIndexTrace, TaskStatus};
use registry::{
pickle::Pickle,
schema::structs::{
Task, TaskIndexTrace, TaskStatus, Trace, TraceEvent, TraceKeyValue, TraceValue,
TraceValueBoolean, TraceValueDuration, TraceValueEvent, TraceValueFloat, TraceValueInteger,
TraceValueIpAddr, TraceValueList, TraceValueString, TraceValueUTCDateTime,
TraceValueUnsignedInt,
},
types::{datetime::UTCDateTime, ipaddr::IpAddr},
};
use std::{collections::HashSet, future::Future, time::Duration};
use store::{
Deserialize, SearchStore, Store, ValueKey,
SearchStore, Store, ValueKey,
search::{IndexDocument, SearchField, SearchFilter, SearchQuery, TracingSearchField},
write::{BatchBuilder, SearchIndex, TelemetryClass, ValueClass},
};
use trc::{
AddContext, AuthEvent, Event, EventDetails, EventType, Key, MessageIngestEvent,
OutgoingReportEvent, QueueEvent, Value,
ipc::subscriber::SubscriberBuilder,
serializers::binary::{deserialize_events, serialize_events},
OutgoingReportEvent, QueueEvent, Value, ipc::subscriber::SubscriberBuilder,
};
use utils::snowflake::SnowflakeIdGenerator;
@@ -53,14 +60,15 @@ pub(crate) fn spawn_store_tracer(builder: SubscriberBuilder, settings: StoreTrac
// Serialize events
batch
.set(
ValueClass::Telemetry(TelemetryClass::Span { span_id }),
serialize_events(
ValueClass::Telemetry(TelemetryClass::Span(span_id)),
map_events(
[span.as_ref()]
.into_iter()
.chain(events.iter().map(|event| event.as_ref()))
.chain([event.as_ref()].into_iter()),
events.len() + 2,
),
)
.to_pickled_vec(),
)
.schedule_task(Task::IndexTrace(TaskIndexTrace {
status: TaskStatus::now(),
@@ -80,15 +88,72 @@ pub(crate) fn spawn_store_tracer(builder: SubscriberBuilder, settings: StoreTrac
});
}
fn map_events<'x>(
span_events: impl IntoIterator<Item = &'x Event<EventDetails>>,
num_events: usize,
) -> Trace {
let mut events = Vec::with_capacity(num_events);
for event in span_events {
let mut key_values = Vec::with_capacity(event.keys.len());
for (key, value) in &event.keys {
key_values.push(TraceKeyValue {
key: *key,
value: map_value(value),
});
}
events.push(TraceEvent {
event: event.inner.typ,
timestamp: UTCDateTime::from_timestamp(event.inner.timestamp as i64),
key_values,
});
}
Trace { events }
}
fn map_value(value: &Value) -> TraceValue {
match value {
Value::String(value) => TraceValue::String(TraceValueString {
value: value.to_string(),
}),
Value::UInt(value) => TraceValue::UnsignedInt(TraceValueUnsignedInt { value: *value }),
Value::Int(value) => TraceValue::Integer(TraceValueInteger { value: *value }),
Value::Float(value) => TraceValue::Float(TraceValueFloat { value: *value }),
Value::Timestamp(value) => TraceValue::UTCDateTime(TraceValueUTCDateTime {
value: UTCDateTime::from_timestamp(*value as i64),
}),
Value::Duration(value) => TraceValue::Duration(TraceValueDuration { value: *value }),
Value::Bytes(items) => TraceValue::String(TraceValueString {
value: String::from_utf8_lossy(items).to_string(),
}),
Value::Bool(value) => TraceValue::Boolean(TraceValueBoolean { value: *value }),
Value::Ipv4(ipv4_addr) => TraceValue::IpAddr(TraceValueIpAddr {
value: IpAddr((*ipv4_addr).into()),
}),
Value::Ipv6(ipv6_addr) => TraceValue::IpAddr(TraceValueIpAddr {
value: IpAddr((*ipv6_addr).into()),
}),
Value::Event(event) => TraceValue::Event(TraceValueEvent {
value: event
.keys()
.iter()
.map(|(k, v)| TraceKeyValue {
key: *k,
value: map_value(v),
})
.collect(),
event: event.event_type(),
}),
Value::Array(values) => TraceValue::List(TraceValueList {
value: values.iter().map(map_value).collect::<Vec<_>>(),
}),
Value::None => TraceValue::Null,
}
}
pub trait TracingStore: Sync + Send {
fn get_span(
&self,
span_id: u64,
) -> impl Future<Output = trc::Result<Vec<Event<EventDetails>>>> + Send;
fn get_raw_span(
&self,
span_id: u64,
) -> impl Future<Output = trc::Result<Option<Vec<u8>>>> + Send;
fn purge_spans(
&self,
period: Duration,
@@ -97,24 +162,6 @@ pub trait TracingStore: Sync + Send {
}
impl TracingStore for Store {
async fn get_span(&self, span_id: u64) -> trc::Result<Vec<Event<EventDetails>>> {
self.get_value::<Span>(ValueKey::from(ValueClass::Telemetry(
TelemetryClass::Span { span_id },
)))
.await
.caused_by(trc::location!())
.map(|span| span.map(|span| span.0).unwrap_or_default())
}
async fn get_raw_span(&self, span_id: u64) -> trc::Result<Option<Vec<u8>>> {
self.get_value::<RawSpan>(ValueKey::from(ValueClass::Telemetry(
TelemetryClass::Span { span_id },
)))
.await
.caused_by(trc::location!())
.map(|span| span.map(|span| span.0))
}
async fn purge_spans(
&self,
period: Duration,
@@ -127,10 +174,8 @@ impl TracingStore for Store {
})?;
self.delete_range(
ValueKey::from(ValueClass::Telemetry(TelemetryClass::Span { span_id: 0 })),
ValueKey::from(ValueClass::Telemetry(TelemetryClass::Span {
span_id: until_span_id,
})),
ValueKey::from(ValueClass::Telemetry(TelemetryClass::Span(0))),
ValueKey::from(ValueClass::Telemetry(TelemetryClass::Span(until_span_id))),
)
.await
.caused_by(trc::location!())?;
@@ -218,76 +263,57 @@ impl StoreTracer {
}
}
struct RawSpan(Vec<u8>);
struct Span(Vec<Event<EventDetails>>);
impl Deserialize for Span {
fn deserialize(bytes: &[u8]) -> trc::Result<Self> {
deserialize_events(bytes).map(Self)
}
}
impl Deserialize for RawSpan {
fn deserialize(bytes: &[u8]) -> trc::Result<Self> {
Ok(Self(bytes.to_vec()))
}
}
pub fn build_span_document(
span_id: u64,
events: Vec<Event<EventDetails>>,
trace: Trace,
index_fields: &AHashSet<SearchField>,
) -> IndexDocument {
let mut document = IndexDocument::new(SearchIndex::Tracing).with_id(span_id);
let mut keywords = HashSet::new();
for (idx, event) in events.into_iter().enumerate() {
for (idx, event) in trace.events.into_iter().enumerate() {
if idx == 0
&& (index_fields.is_empty()
|| index_fields.contains(&TracingSearchField::EventType.into()))
{
document.index_unsigned(TracingSearchField::EventType, event.inner.typ.to_id());
document.index_unsigned(TracingSearchField::EventType, event.event.to_id());
}
for (key, value) in event.keys {
for TraceKeyValue { key, value } in event.key_values {
match (key, value) {
(Key::QueueId, Value::UInt(queue_id)) => {
(Key::QueueId, TraceValue::UnsignedInt(TraceValueUnsignedInt { value })) => {
if index_fields.is_empty()
|| index_fields.contains(&TracingSearchField::QueueId.into())
{
document.index_unsigned(TracingSearchField::QueueId, queue_id);
document.index_unsigned(TracingSearchField::QueueId, value);
}
}
(Key::From | Key::To | Key::Domain | Key::Hostname, Value::String(address)) => {
(
Key::From | Key::To | Key::Domain | Key::Hostname,
TraceValue::String(TraceValueString { value }),
) => {
if index_fields.is_empty()
|| index_fields.contains(&TracingSearchField::Keywords.into())
{
keywords.insert(address.to_string());
keywords.insert(value);
}
}
(Key::To, Value::Array(value)) => {
(Key::To, TraceValue::List(TraceValueList { value })) => {
if index_fields.is_empty()
|| index_fields.contains(&TracingSearchField::Keywords.into())
{
for value in value {
if let Value::String(address) = value {
keywords.insert(address.to_string());
if let TraceValue::String(TraceValueString { value }) = value {
keywords.insert(value);
}
}
}
}
(Key::RemoteIp, Value::Ipv4(ip)) => {
(Key::RemoteIp, TraceValue::IpAddr(TraceValueIpAddr { value })) => {
if index_fields.is_empty()
|| index_fields.contains(&TracingSearchField::Keywords.into())
{
keywords.insert(ip.to_string());
}
}
(Key::RemoteIp, Value::Ipv6(ip)) => {
if index_fields.is_empty()
|| index_fields.contains(&TracingSearchField::Keywords.into())
{
keywords.insert(ip.to_string());
keywords.insert(value.to_string());
}
}