Skip to main content
Glama

Convex MCP server

Official
by get-convex
metrics.rs20 kB
use metrics::{ cluster_label, log_counter, log_counter_with_labels, log_distribution, log_distribution_with_labels, log_gauge_with_labels, register_convex_counter, register_convex_gauge, register_convex_histogram, IntoLabel, StaticMetricLabel, StatusTimer, Timer, CLUSTER_LABEL, STATUS_LABEL, }; use prometheus::VMHistogram; use crate::{ query::{ CompiledQuery, RevisionWithKeys, }, scoring::Bm25StatisticsDiff, tantivy_query::SearchQueryResult, SearchFileType, TantivyDocument, TantivySearchIndexSchema, }; register_convex_histogram!( SEARCH_INDEX_INTO_TANTIVY_DOCUMENT_SECONDS, "Time taken to generate a Tantivy document" ); pub fn index_into_tantivy_document_timer() -> Timer<VMHistogram> { Timer::new(&SEARCH_INDEX_INTO_TANTIVY_DOCUMENT_SECONDS) } register_convex_histogram!( SEARCH_INDEX_MANAGER_UPDATE_SECONDS, "Duration of a search index update", &STATUS_LABEL ); pub fn index_manager_update_timer() -> StatusTimer { StatusTimer::new(&SEARCH_INDEX_MANAGER_UPDATE_SECONDS) } register_convex_histogram!( SEARCH_INDEX_UPDATE_SECONDS, "Duration of updating a single in-memory index", &STATUS_LABEL ); pub fn index_update_timer() -> StatusTimer { StatusTimer::new(&SEARCH_INDEX_UPDATE_SECONDS) } register_convex_histogram!( SEARCH_INDEX_QUERY_TOKENS_SECONDS, "Duration of querying tokens in memory index", ); pub fn index_query_tokens_timer() -> Timer<VMHistogram> { Timer::new(&SEARCH_INDEX_QUERY_TOKENS_SECONDS) } register_convex_histogram!( SEARCH_INDEX_UPDATE_BM25_STATS_SECONDS, "Duration of updating BM25 stats in memory index", ); pub fn index_update_bm25_stats_timer() -> Timer<VMHistogram> { Timer::new(&SEARCH_INDEX_UPDATE_BM25_STATS_SECONDS) } register_convex_histogram!( SEARCH_INDEX_PREPARE_POSTING_LIST_QUERY_SECONDS, "Duration of preparing posting list query in memory index", ); pub fn index_prepare_posting_list_query_timer() -> Timer<VMHistogram> { Timer::new(&SEARCH_INDEX_PREPARE_POSTING_LIST_QUERY_SECONDS) } register_convex_histogram!( SEARCH_INDEX_QUERY_TOMBSTONES_SECONDS, "Duration of querying tombstones in memory index", ); pub fn index_query_tombstones_timer() -> Timer<VMHistogram> { Timer::new(&SEARCH_INDEX_QUERY_TOMBSTONES_SECONDS) } register_convex_histogram!( SEARCH_INDEX_QUERY_POSTING_LISTS_SECONDS, "Duration of querying posting lists in memory index", ); pub fn index_query_posting_lists_timer() -> Timer<VMHistogram> { Timer::new(&SEARCH_INDEX_QUERY_POSTING_LISTS_SECONDS) } register_convex_histogram!(SEARCH_TERM_TEXT_BYTES, "Size of search terms"); pub fn log_text_term(term: &str) { log_distribution(&SEARCH_TERM_TEXT_BYTES, term.len() as f64); } register_convex_histogram!(SEARCH_TERM_FILTER_BYTES, "Size of search filters"); pub fn log_filter_term(term: &[u8]) { log_distribution(&SEARCH_TERM_FILTER_BYTES, term.len() as f64); } register_convex_histogram!( SEARCH_INDEX_INTO_TERMS_SECONDS, "Time to process a document into terms" ); pub fn index_into_terms_timer() -> Timer<VMHistogram> { Timer::new(&SEARCH_INDEX_INTO_TERMS_SECONDS) } register_convex_counter!( SEARCH_UPDATE_INDEX_CREATED_TOTAL, "Number of search indexes created" ); pub fn log_index_created() { log_counter(&SEARCH_UPDATE_INDEX_CREATED_TOTAL, 1); } register_convex_counter!( SEARCH_UPDATE_INDEX_BACKFILLED_TOTAL, "Number of search indexes backfilled" ); pub fn log_index_backfilled() { log_counter(&SEARCH_UPDATE_INDEX_BACKFILLED_TOTAL, 1); } register_convex_counter!( SEARCH_UPDATE_INDEX_ADVANCED_TOTAL, "Number of search indexes advanced in time" ); pub fn log_index_advanced() { log_counter(&SEARCH_UPDATE_INDEX_ADVANCED_TOTAL, 1); } register_convex_counter!( SEARCH_UPDATE_INDEX_DELETED_TOTAL, "Number of search index deletions" ); pub fn log_index_deleted() { log_counter(&SEARCH_UPDATE_INDEX_DELETED_TOTAL, 1); } register_convex_histogram!( SEARCH_INDEX_MANAGER_SEARCH_SECONDS, "Total search duration", &[STATUS_LABEL[0], CLUSTER_LABEL], ); pub fn search_timer(cluster: &'static str) -> StatusTimer { let mut timer = StatusTimer::new(&SEARCH_INDEX_MANAGER_SEARCH_SECONDS); timer.add_label(cluster_label(cluster)); timer } register_convex_histogram!( SEARCH_INDEX_MANAGER_RESULTS_TOTAL, "Number of results from the search index manager" ); pub fn finish_search(timer: StatusTimer, revisions_with_keys: &RevisionWithKeys) { log_distribution( &SEARCH_INDEX_MANAGER_RESULTS_TOTAL, revisions_with_keys.len() as f64, ); timer.finish(); } register_convex_histogram!( SEARCH_SCHEMA_COMPILE_SECONDS, "Time to compile a search schema", &STATUS_LABEL ); pub fn compile_timer() -> StatusTimer { StatusTimer::new(&SEARCH_SCHEMA_COMPILE_SECONDS) } register_convex_counter!( SEARCH_SCHEMA_COMPILE_TEXT_TERMS_TOTAL, "Number of text terms in a compiled search query" ); register_convex_counter!( SEARCH_SCHEMA_COMPILE_FILTER_TERMS_TOTAL, "Number of filter terms in a compiled search query" ); pub fn log_compiled_query(query: &CompiledQuery) { log_counter( &SEARCH_SCHEMA_COMPILE_TEXT_TERMS_TOTAL, query.text_query.len() as u64, ); log_counter( &SEARCH_SCHEMA_COMPILE_FILTER_TERMS_TOTAL, query.filter_conditions.len() as u64, ); } register_convex_counter!( SEARCH_EXCEEDED_TOKEN_LIMIT_TOTAL, "The number of times a search query had more tokens than our limit" ); pub fn log_search_token_limit_exceeded() { log_counter(&SEARCH_EXCEEDED_TOKEN_LIMIT_TOTAL, 1) } register_convex_histogram!( SEARCH_BM25_STATISTICS_DIFF_SECONDS, "Time to compute a BM25 diff", &STATUS_LABEL ); pub fn bm25_statistics_diff_timer() -> StatusTimer { StatusTimer::new(&SEARCH_BM25_STATISTICS_DIFF_SECONDS) } register_convex_counter!( SEARCH_BM25_TERM_DOC_FREQ_DIFF_TOTAL, "Number of documents with a BM25 term diff" ); register_convex_counter!( SEARCH_BM25_NUM_DOCS_DIFF_TOTAL, "Total number of documents in the BM25 diff" ); register_convex_counter!( SEARCH_BM25_NUM_SEARCH_TERMS_DIFF_TOTAL, "Total number of tokens in the BM25 diff" ); pub fn log_bm25_statistics_diff(timer: StatusTimer, diff: &Bm25StatisticsDiff) { for num_docs_with_term_diff in diff.term_statistics.values() { log_counter( &SEARCH_BM25_TERM_DOC_FREQ_DIFF_TOTAL, *num_docs_with_term_diff as u64, ); } log_counter( &SEARCH_BM25_NUM_DOCS_DIFF_TOTAL, diff.num_documents_diff as u64, ); log_counter( &SEARCH_BM25_NUM_SEARCH_TERMS_DIFF_TOTAL, diff.num_search_tokens_diff as u64, ); timer.finish(); } register_convex_histogram!( SEARCH_TOTAL_NUM_DOCUMENTS_AND_TOKENS_SECONDS, "Time to compute the total number of documents and tokens in memory" ); pub fn total_num_documents_and_tokens_timer() -> Timer<VMHistogram> { Timer::new(&SEARCH_TOTAL_NUM_DOCUMENTS_AND_TOKENS_SECONDS) } register_convex_histogram!( SEARCH_NUM_DOCUMENTS_WITH_TERM_SECONDS, "Time to compute the number of documents containing a term" ); pub fn num_documents_with_term_timer() -> Timer<VMHistogram> { Timer::new(&SEARCH_NUM_DOCUMENTS_WITH_TERM_SECONDS) } register_convex_histogram!( SEARCH_SEARCHLIGHT_CLIENT_EXECUTE_SECONDS, "Time to execute a query against Searchlight", &[STATUS_LABEL[0], CLUSTER_LABEL] ); pub fn searchlight_client_execute_timer(cluster: &'static str) -> StatusTimer { let mut timer = StatusTimer::new(&SEARCH_SEARCHLIGHT_CLIENT_EXECUTE_SECONDS); timer.add_label(cluster_label(cluster)); timer } register_convex_histogram!( SEARCH_SEARCHLIGHT_OVERFETCH_DELTA_TOTAL, "Size of the searchlight overfetch delta" ); pub fn log_searchlight_overfetch_delta(overfetch_delta: usize) { log_distribution( &SEARCH_SEARCHLIGHT_OVERFETCH_DELTA_TOTAL, overfetch_delta as f64, ); } register_convex_histogram!( SEARCH_NUM_DISCARDED_REVISIONS_TOTAL, "Number of discarded revisions" ); pub fn log_num_discarded_revisions(discarded_revisions: usize) { log_distribution( &SEARCH_NUM_DISCARDED_REVISIONS_TOTAL, discarded_revisions as f64, ); } register_convex_counter!( SEARCH_SEARCH_TERM_EDIT_DISTANCE_TOTAL, "Number of times a search term was edited", &["distance", "prefix"] ); pub fn log_search_term_edit_distance(distance: u32, prefix: bool) { log_counter_with_labels( &SEARCH_SEARCH_TERM_EDIT_DISTANCE_TOTAL, 1, vec![ StaticMetricLabel::new("distance", distance.to_string()), StaticMetricLabel::new("prefix", prefix.to_string()), ], ); } register_convex_histogram!( SEARCH_SEARCHLIGHT_CLIENT_RESULTS_TOTAL, "Number of results from Searchlight" ); pub fn finish_searchlight_client_execute(timer: StatusTimer, result: &SearchQueryResult) { log_distribution( &SEARCH_SEARCHLIGHT_CLIENT_RESULTS_TOTAL, result.results.len() as f64, ); timer.finish(); } register_convex_histogram!( SEARCH_MEMORY_QUERY_SECONDS, "Time to execute a search query against the memory index", &STATUS_LABEL ); pub fn memory_query_timer() -> StatusTimer { StatusTimer::new(&SEARCH_MEMORY_QUERY_SECONDS) } register_convex_histogram!( SEARCH_INDEX_MEMORY_QUERY_RESULTS_TOTAL, "Number of results from querying the in-memory search index" ); pub fn finish_memory_query(timer: StatusTimer, revisions_len: usize) { log_distribution( &SEARCH_INDEX_MEMORY_QUERY_RESULTS_TOTAL, revisions_len as f64, ); timer.finish(); } register_convex_histogram!( SEARCH_MEMORY_UPDATED_MATCHES_SECONDS, "Time to update matches in the memory search index", &STATUS_LABEL ); pub fn updated_matches_timer() -> StatusTimer { StatusTimer::new(&SEARCH_MEMORY_UPDATED_MATCHES_SECONDS) } register_convex_histogram!( SEARCH_INDEX_READER_FOR_DIRECTORY_SECONDS, "Time to get a Tantivy IndexReader for a directory", &STATUS_LABEL ); pub fn index_reader_for_directory_timer() -> StatusTimer { StatusTimer::new(&SEARCH_INDEX_READER_FOR_DIRECTORY_SECONDS) } register_convex_histogram!( SEARCH_QUERY_TANTIVY_SECONDS, "Total time to execute a query against Tantivy", &STATUS_LABEL ); pub fn query_tantivy_timer() -> StatusTimer { StatusTimer::new(&SEARCH_QUERY_TANTIVY_SECONDS) } register_convex_histogram!( SEARCH_QUERY_TANTIVY_SEGMENTS_TOTAL, "Number of segments in the Tantivy index" ); pub fn log_num_segments(num_segments: usize) { log_distribution(&SEARCH_QUERY_TANTIVY_SEGMENTS_TOTAL, num_segments as f64); } register_convex_histogram!( SEARCH_QUERY_TANTIVY_RESULTS_TOTAL, "Number of results from Tantivy" ); pub fn finish_query_tantivy(timer: StatusTimer, revisions_len: usize) { log_distribution(&SEARCH_QUERY_TANTIVY_RESULTS_TOTAL, revisions_len as f64); timer.finish(); } register_convex_histogram!( SEARCH_QUERY_TANTIVY_STATISTICS_SECONDS, "Time to query Tantivy statistics", &STATUS_LABEL ); pub fn query_tantivy_statistics_timer() -> StatusTimer { StatusTimer::new(&SEARCH_QUERY_TANTIVY_STATISTICS_SECONDS) } register_convex_histogram!( SEARCH_QUERY_TANTIVY_SEARCH_SECONDS, "Time to collect Tantivy search results", &STATUS_LABEL ); pub fn query_tantivy_search_timer() -> StatusTimer { StatusTimer::new(&SEARCH_QUERY_TANTIVY_SEARCH_SECONDS) } register_convex_histogram!( SEARCH_QUERY_TANTIVY_FAST_FIELD_SECONDS, "Time to query Tantivy fast fields", &STATUS_LABEL ); pub fn query_tantivy_fast_field_timer() -> StatusTimer { StatusTimer::new(&SEARCH_QUERY_TANTIVY_FAST_FIELD_SECONDS) } register_convex_histogram!( SEARCH_QUERY_READS_OVERLAPS_SECONDS, "Time to compute if a read query overlaps with a document" ); pub fn query_reads_overlaps_timer() -> Timer<VMHistogram> { Timer::new(&SEARCH_QUERY_READS_OVERLAPS_SECONDS) } register_convex_histogram!( SEARCH_QUERY_READS_OVERLAPS_SEARCH_VALUE_SECONDS, "Time to compute if a read query overlaps with a search value" ); pub fn query_reads_overlaps_search_value_timer() -> Timer<VMHistogram> { Timer::new(&SEARCH_QUERY_READS_OVERLAPS_SEARCH_VALUE_SECONDS) } register_convex_counter!( SEARCH_QUERY_READS_OVERLAPS_TOTAL, "Number of query reads and whether or not they overlapped a document", &["overlaps"] ); pub fn log_query_reads_outcome(overlaps: bool) { log_counter_with_labels( &SEARCH_QUERY_READS_OVERLAPS_TOTAL, 1, vec![StaticMetricLabel::new("overlaps", overlaps.as_label())], ); } register_convex_histogram!( VECTOR_COMPACTION_COMPACT_SECONDS_TOTAL, "The amount of time spent actually compacting segments", &STATUS_LABEL, ); pub fn vector_compact_seconds_timer() -> StatusTimer { StatusTimer::new(&VECTOR_COMPACTION_COMPACT_SECONDS_TOTAL) } register_convex_histogram!( VECTOR_COMPACTION_FETCH_SEGMENTS_SECONDS_TOTAL, "The amount of time spent fetching segments to compact", &STATUS_LABEL, ); pub fn vector_compact_fetch_segments_seconds_timer() -> StatusTimer { StatusTimer::new(&VECTOR_COMPACTION_FETCH_SEGMENTS_SECONDS_TOTAL) } register_convex_histogram!( VECTOR_COMPACTION_CONSTRUCT_SEGMENTS_SECONDS_TOTAL, "The amount of time spent compacting segments after they've been fetched", &STATUS_LABEL, ); pub fn vector_compact_construct_segment_seconds_timer() -> StatusTimer { StatusTimer::new(&VECTOR_COMPACTION_CONSTRUCT_SEGMENTS_SECONDS_TOTAL) } register_convex_histogram!( VECTOR_COMPACTION_VECTORS_IN_SEGMENT_TOTAL, "The total number of vectors in the newly created compacted segment" ); pub fn log_vectors_in_compacted_segment_total(num_vectors: u32) { log_distribution( &VECTOR_COMPACTION_VECTORS_IN_SEGMENT_TOTAL, num_vectors as f64, ); } register_convex_histogram!( COMPACTION_COMPACTED_SEGMENT_SIZE_BYTES, "The total size of the newly created compacted segment", &[SEARCH_TYPE_LABEL], ); pub fn log_compacted_segment_size_bytes(size_bytes: u64, search_type: SearchType) { log_distribution_with_labels( &COMPACTION_COMPACTED_SEGMENT_SIZE_BYTES, size_bytes as f64, vec![search_type.tag()], ); } pub const SEARCH_FILE_TYPE: &str = "search_file_type"; impl SearchFileType { pub fn metric_label(&self) -> StaticMetricLabel { let search_type_str = match self { SearchFileType::VectorSegment => "vector_segment", SearchFileType::VectorDeletedBitset => "vector_deleted_bitset", SearchFileType::VectorIdTracker => "vector_id_tracker", SearchFileType::Text => "text", SearchFileType::TextIdTracker => "text_id_tracker", SearchFileType::TextAliveBitset => "text_alive_bitset", SearchFileType::TextDeletedTerms => "text_deleted_terms", SearchFileType::FragmentedVectorSegment => "fragmented_vector_segment", }; StaticMetricLabel::new(SEARCH_FILE_TYPE, search_type_str) } } register_convex_histogram!( SEARCH_UPLOAD_ARCHIVE_SECONDS, "Amount of time it takes to upload an archive", &[STATUS_LABEL[0], SEARCH_FILE_TYPE], ); pub fn upload_archive_timer(search_file_type: SearchFileType) -> StatusTimer { let mut timer = StatusTimer::new(&SEARCH_UPLOAD_ARCHIVE_SECONDS); timer.add_label(search_file_type.metric_label()); timer } register_convex_histogram!( DATABASE_SEARCH_DOCUMENT_INDEXED_SEARCH_BYTES, "Size of search fields in a text index" ); register_convex_histogram!( DATABASE_SEARCH_DOCUMENT_INDEXED_FILTER_BYTES, "Size of filter fields in a text index" ); pub fn log_text_document_indexed(schema: &TantivySearchIndexSchema, document: &TantivyDocument) { let lengths = schema.document_lengths(document); log_distribution( &DATABASE_SEARCH_DOCUMENT_INDEXED_SEARCH_BYTES, lengths.search_field as f64, ); for (_, filter_len) in lengths.filter_fields { log_distribution( &DATABASE_SEARCH_DOCUMENT_INDEXED_FILTER_BYTES, filter_len as f64, ); } } #[derive(Clone, Copy, Debug, strum::AsRefStr)] pub enum SearchType { Vector, Text, } impl SearchType { pub fn tag(self) -> StaticMetricLabel { search_type_label(self) } } pub const SEARCH_TYPE_LABEL: &str = "search_type"; pub fn search_type_label(search_type: SearchType) -> StaticMetricLabel { let type_str = match search_type { SearchType::Vector => "vector", SearchType::Text => "text", }; StaticMetricLabel::new("search_type", type_str) } register_convex_counter!( SEARCHLIGHT_ASYNC_LRU_CACHE_HIT_TOTAL, "Count of requests which had a result ready in the archive cache", &[ASYNC_LRU_LABEL], ); pub fn log_async_lru_cache_hit(label: &str) { log_counter_with_labels( &SEARCHLIGHT_ASYNC_LRU_CACHE_HIT_TOTAL, 1, vec![async_lru_label(label)], ); } pub const ASYNC_LRU_LABEL: &str = "label"; pub fn async_lru_label(label: &str) -> StaticMetricLabel { StaticMetricLabel::new(ASYNC_LRU_LABEL, label.to_owned()) } register_convex_counter!( SEARCHLIGHT_ASYNC_LRU_CACHE_WAITING_TOTAL, "Count of requests which waited on a result to become ready in the archive cache", &[ASYNC_LRU_LABEL], ); pub fn log_async_lru_cache_waiting(label: &str) { log_counter_with_labels( &SEARCHLIGHT_ASYNC_LRU_CACHE_WAITING_TOTAL, 1, vec![async_lru_label(label)], ); } register_convex_counter!( SEARCHLIGHT_ASYNC_LRU_CACHE_MISS_TOTAL, "Count of requests which had to fetch the archive as the cache missed", &[ASYNC_LRU_LABEL], ); pub fn log_async_lru_cache_miss(label: &str) { log_counter_with_labels( &SEARCHLIGHT_ASYNC_LRU_CACHE_MISS_TOTAL, 1, vec![async_lru_label(label)], ); } register_convex_gauge!( SEARCHLIGHT_ASYNC_LRU_SIZE_TOTAL, "Number of entries in a searchlight async LRU", &[ASYNC_LRU_LABEL], ); pub fn log_async_lru_size(size: usize, label: &str) { log_gauge_with_labels( &SEARCHLIGHT_ASYNC_LRU_SIZE_TOTAL, size as f64, vec![async_lru_label(label)], ) } register_convex_histogram!( VECTOR_PREFETCH_SECONDS, "Time to prefetch a vector segment in searchlight once it reaches the front of the queue", &STATUS_LABEL, ); pub fn vector_prefetch_timer() -> StatusTimer { StatusTimer::new(&VECTOR_PREFETCH_SECONDS) } register_convex_counter!( VECTOR_PREFETCH_REJECTIONS_TOTAL, "Count of number of rejected prefetch requests due to the queue being full", ); pub fn log_vector_prefetch_rejection() { log_counter(&VECTOR_PREFETCH_REJECTIONS_TOTAL, 1); } register_convex_counter!( VECTOR_PREFETCH_EXPIRATIONS_TOTAL, "Count of number of expired prefetch requests due codel queue expiration", ); pub fn log_vector_prefetch_expiration() { log_counter(&VECTOR_PREFETCH_EXPIRATIONS_TOTAL, 1); } register_convex_histogram!( TEXT_SEARCH_NUMBER_OF_SEGMENTS_TOTAL, "Number of text segments searched for a multi segment text index" ); pub fn log_num_segments_searched_total(num_segments: usize) { log_distribution(&TEXT_SEARCH_NUMBER_OF_SEGMENTS_TOTAL, num_segments as f64); } register_convex_counter!( SEARCH_MISSING_INDEX_KEY_TOTAL, "Number of times an index was not found in DocumentIndexKeys" ); pub fn log_missing_index_key() { // See the comment in log_missing_index_key_staleness (database::metrics) log_counter(&SEARCH_MISSING_INDEX_KEY_TOTAL, 1); } register_convex_counter!( SEARCH_MISSING_FILTER_VALUE_TOTAL, "Number of times a filter value was not found in SearchIndexKeyValue" ); pub fn log_missing_filter_value() { log_counter(&SEARCH_MISSING_FILTER_VALUE_TOTAL, 1); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/get-convex/convex-backend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server