Skip to content

Reference

Metrics

The engine registers Micrometer meters to monitor search performance, indexing, synchronization, cluster state, and node resources.

You can scrape these meters through the Prometheus endpoint or push them to an OpenTelemetry (OTLP) collector.

NameTypeUnitTagsDescription
exofind.searchTimerSecondsoutcome (success, error), index (optional)End-to-end duration of a search request. The index tag is present only when EXOFIND_METRICS_INDEX_SEARCH_HISTOGRAM is true.
exofind.search.relaxationCounterWordsreasonCount of words dropped by a search before the query matched. Each dropped word increments the counter by one.
exofind.search.interpretationCounterFilterskind (number, value)Count of filters a search read out of the text a user typed. number counts a price bound or another quantity, and value counts a word read as a value of a field, such as a colour. Each filter read increments the counter by one.
exofind.suggestTimerSecondsoutcome (success, error), index (optional)End-to-end duration of a suggest request (POST /v1alpha1/indexes/{name}/suggest). The index tag is present only when EXOFIND_METRICS_INDEX_SEARCH_HISTOGRAM is true, the same switch as exofind.search.
exofind.facet.valuesTimerSecondsoutcome (success, error), index (optional)End-to-end duration of a request for the values of one facet (POST /v1alpha1/indexes/{name}/facets/{field}/values). Kept apart from exofind.search because a filter panel completing a value asks on every keystroke. The index tag is present only when EXOFIND_METRICS_INDEX_SEARCH_HISTOGRAM is true, the same switch as exofind.search.
exofind.explainTimerSecondsoutcome (success, error), index (optional)End-to-end duration of an explanation of one hit (POST /v1alpha1/indexes/{name}/search/actions/explain). The index tag is present only when EXOFIND_METRICS_INDEX_SEARCH_HISTOGRAM is true, the same switch as exofind.search.
exofind.search.piecesCounterPiecesthread (pool, request)Count of search work pieces, where a piece is one segment collected or one part of the facet counting. The thread tag indicates whether the piece ran on a search pool thread (pool) or on the request thread (request). Pieces run under request while EXOFIND_SEARCH_THREADS holds threads mean the pool was busy. Lucene’s own ranking slices are not counted.
NameTypeUnitTagsDescription
exofind.writeTimerSecondsoperation (add, update, delete, delete_by_query), outcome (success, error)Duration of a write request on the node that served it. The duration covers reading the request and changing the index, and excludes finding the index.
exofind.write.documentsCounterDocumentsoperation (add, update, delete, delete_by_query)Count of documents processed in write requests. A request that failed counts none, including the documents it wrote before the failure.
exofind.write.forwardedCounterRequestsoutcome (success, unreachable, unavailable, stale)Write requests forwarded to the node holding the index.
exofind.commitTimerSecondstrigger (changes, interval, merges, explicit, freshness), outcome (success, error)Duration of a Lucene commit operation. A freshness trigger is a commit that a search waiting on a freshness token asked the writer for.

The operation tag names the request rather than the endpoint, so the JSON and the newline-delimited form of one request share a value:

  • add: indexing documents, and counting every document the request carried.
  • update: changing fields of documents, by batch or by key, and counting the documents that changed. A key skipped with ?missing=skip counts as none.
  • delete: removing documents by key, and counting the keys the request carried. A key nothing was indexed under counts all the same.
  • delete_by_query: removing documents by query or emptying the index with all, and counting the committed documents the removal matched.
NameTypeUnitTagsDescription
exofind.sync.pushTimerSecondsoutcome (success, error)Duration of pushing an index to remote storage.
exofind.sync.pullTimerSecondsoutcome (success, error)Duration of pulling an index from remote storage.
exofind.sync.conflictCounterConflictsoperation (push, pull)Synchronization attempts refused because another node wrote to the index. A value above zero indicates concurrent writers.
exofind.storage.operationTimerSecondsoperation, outcome (success, error), statusDuration of an object storage request. The operation tag contains the S3 API call (such as GetObject or PutObject). The status tag contains the HTTP status code (such as 412).
NameTypeUnitTagsDescription
exofind.indexes.openGaugeGenerationsNoneNumber of index generations currently open on this node.
exofind.indexes.ownedGaugeIndexesNoneNumber of index names currently written by this node.
exofind.indexes.totalGaugeIndexesNoneTotal index names across the deployment, as read during the last refresh pass.
exofind.index.stateGaugeGenerationsstate (NEEDS_PULL, PULLING, USABLE, MODIFIED, PUSHING, UNSUPPORTED, INCOMPATIBLE, CLOSED)Number of open generations in each synchronization state. Emits one series per state across all indexes.
exofind.ownership.changeCounterEventsdirection (gained, lost, revoked)Changes in index write ownership on this node.
exofind.registry.refresh.ageGaugeSecondsNoneSeconds elapsed since the registry refresh loop completed a pass.
exofind.indexes.openedCounterGenerationssource (request, preload, background)Generations opened on this node, counted when the generation finished opening. request counts the opens a search or a write waited for, preload counts the opens the startup preload made before anything asked, and background counts the opens the node made for itself, such as gaining an index to write or creating a generation.
exofind.indexes.open.waitTimerSecondsoutcome (success, error)Duration a request waited for the generation it asked for to be opened, covering the pull, the first reads of a copy already on disk, and the wait where another thread was opening the same generation. Recorded only when the generation was not already open, so the count is the number of requests that waited rather than the number of requests served.

The state tag carries the uppercase name of the state. The admin API answers with the lowercase form of the same name, such as needs_pull. See Index states.

NameTypeUnitTagsDescription
exofind.index.unhealthyGaugeValue (1)index, generation, state (NEEDS_PULL, PULLING, MODIFIED, PUSHING, UNSUPPORTED, INCOMPATIBLE, CLOSED)Reports 1 for each open generation that is not in the USABLE state. Emits no series when all generations are healthy.
exofind.index.documentsGaugeDocumentsindex, generationNumber of documents contained in the index generation. Registered only on the writer node.
exofind.index.pending.changesGaugeChangesindex, generationNumber of uncommitted changes waiting for a Lucene commit. Registered only on the writer node.
exofind.index.pending.ageGaugeSecondsindex, generationAge in seconds of the oldest uncommitted change. Registered only on the writer node.
exofind.index.disk.bytesGaugeBytesindex, generationDisk space occupied by the index generation in the local index directory. Registered on all nodes holding a copy.
NameTypeUnitTagsDescription
exofind.disk.used.bytesGaugeBytesNoneTotal disk space used by the local index directory.
exofind.disk.max.bytesGaugeBytesNoneConfigured maximum disk capacity for the index directory (EXOFIND_INDEXES_DISK_MAX_SIZE). Absent when no limit is configured.
exofind.document.cache.hitsCounterReadsNoneNumber of document reads served directly from the in-memory cache.
exofind.document.cache.missesCounterReadsNoneNumber of document reads that missed the cache and required a disk lookup.
exofind.document.cache.evictionsCounterEntriesNoneNumber of entries evicted from the document cache.
exofind.query.cache.hitsCounterClausesNoneNumber of narrowing clauses answered from the matching documents the query cache kept for a segment.
exofind.query.cache.missesCounterClausesNoneNumber of narrowing clauses the query cache held no matches for. A clause is only kept once the searches of its index have asked for it more than once recently, so a miss does not always mean an entry was made.
exofind.query.cache.evictionsCounterQueriesNoneNumber of queries the query cache dropped to stay within EXOFIND_SEARCH_QUERY_CACHE_MAX_QUERIES and EXOFIND_SEARCH_QUERY_CACHE_MAX_SIZE.
exofind.query.cache.bytesGaugeBytesNoneHeap the matches the query cache holds take.
exofind.term.cache.hitsCounterLookupsNoneNumber of term lookups answered from where the term cache already knew a term sits in its reader.
exofind.term.cache.missesCounterLookupsNoneNumber of term lookups that had to seek the term in every segment of the reader.
exofind.term.cache.evictionsCounterTermsNoneNumber of terms the term cache dropped to stay within EXOFIND_SEARCH_TERM_CACHE_MAX_SIZE. Terms of a reader that closed are removed, not evicted.
exofind.typo.cache.hitsCounterWordsNoneNumber of typo tolerant words answered with an automaton compiled earlier.
exofind.typo.cache.missesCounterWordsNoneNumber of typo tolerant words whose automaton had to be compiled.
exofind.prefix.cache.hitsCounterWordsNoneNumber of half typed words answered with an automaton compiled earlier.
exofind.prefix.cache.missesCounterWordsNoneNumber of half typed words whose automaton had to be compiled.
exofind.facet.cache.hitsCounterFacetsNoneNumber of facets answered from the counts an earlier search made over the same scope: the same clauses, locale, settings and definition against the same reader.
exofind.facet.cache.missesCounterFacetsNoneNumber of facets that had to be counted. The hit rate over a period is hits / (hits + misses).
exofind.facet.cache.evictionsCounterEntriesNoneNumber of facet scope entries dropped to stay within EXOFIND_SEARCH_FACET_CACHE_MAX_SIZE. Entries of a reader that closed are removed, not evicted.
exofind.facet.segment.hitsCounterSegmentsNoneNumber of segments whose counts over everything the index holds were reused. A refresh keeps the counts of the segments it left untouched, so a search after it only counts the new segments.
exofind.facet.segment.missesCounterSegmentsNoneNumber of segments a facet had to count over everything the index holds.
exofind.facet.state.bytesGaugeBytesNoneEstimated heap memory used by the facet state of all open readers: ordinal maps, segment columns and postings, and counts across each segment.
exofind.facet.warmTimerSecondsoutcome (success, superseded, stopped, error)Duration of preparing the facet state of a reopened reader before a search requests it. The superseded outcome is recorded when a newer reader replaced the one being prepared before the work finished, stopped when the node shut down before it finished, and error when the preparation failed, after which the first search builds the state itself.
exofind.facet.warm.queuedGaugeIndexesNoneNumber of indexes waiting for a warm thread to prepare their latest reader. A value that stays high means the warm threads do not keep up with how often readers reopen, so the first search after a reopen builds the state itself.
NameTypeUnitTagsDescription
exofind.api.errorCounterRequestscodeAPI requests that resulted in an error response, tagged with the API error code.
exofind.auth.failureCounterRequestsreason (unauthenticated, forbidden, not_covered)API requests refused during authentication or authorization.
NameTypeUnitTagsDescription
exofind.reindex.activeGaugeJobsphase (PENDING, COPYING, REPLAYING, READY, PROMOTING, DONE, FAILED, CANCELLED)Number of reindexing jobs known to this node in each phase. Emits one series per phase across all indexes.

To limit cardinality growth in large deployments, meters differ in whether they include index names and on which nodes they register:

MeterCarries index tagRegistration scope
exofind.searchOptional (off by default)All nodes serving search requests
exofind.suggestOptional (off by default)All nodes serving search requests
exofind.facet.valuesOptional (off by default)All nodes serving search requests
exofind.explainOptional (off by default)All nodes serving search requests
exofind.index.unhealthyYesNodes with generations not in the USABLE state
exofind.index.documentsYesNode currently writing the index
exofind.index.pending.changesYesNode currently writing the index
exofind.index.pending.ageYesNode currently writing the index
exofind.index.disk.bytesYesEvery node holding a local copy
All other metersNoNode-level

The engine includes two Micrometer registries on the classpath:

  • Prometheus: Exposed at /q/metrics on the node HTTP port. Enabled by default. This endpoint does not require authentication keys. Scraping this endpoint consumes resources only when requested.
  • OTLP push: Compiled in but disabled at runtime by default.

OTLP push is built into the node binary (quarkus.micrometer.export.otlp.enabled=true). You configure export at runtime using environment variables:

VariableDefaultDescription
QUARKUS_MICROMETER_EXPORT_OTLP_PUBLISHfalseSet to true to enable pushing metrics to an OTLP collector.
QUARKUS_MICROMETER_EXPORT_OTLP_URLhttp://localhost:4318/v1/metricsTarget OTLP endpoint URL. For example, https://<collector>/v1/metrics.
QUARKUS_MICROMETER_EXPORT_OTLP_STEP60sPush interval frequency.
QUARKUS_MICROMETER_EXPORT_OTLP_HEADERSNoneComma-separated list of key=value headers, such as authentication tokens for hosted gateways.

The engine publishes cumulative histogram buckets rather than precomputed quantiles. Buckets from multiple nodes or label dimensions can be summed with sum by (le) in Prometheus, whereas quantiles cannot be aggregated across series.

The EXOFIND_METRICS_HISTOGRAM_MODE setting supports three modes:

  • slo (default): Publishes fixed bucket boundaries tailored for service level objectives.
  • detailed: Publishes Micrometer default percentiles histogram bucket set (tens of buckets per timer). Suitable for backends supporting native histograms.
  • none: Publishes only count, total time, and maximum value. Emits no histogram buckets.

Request timers (exofind.search, exofind.suggest, exofind.facet.values, exofind.explain, exofind.write):

  • 1 ms
  • 5 ms
  • 10 ms
  • 25 ms
  • 50 ms
  • 100 ms
  • 250 ms
  • 500 ms
  • 1 s
  • 5 s
  • 10 s

Synchronization and storage timers (exofind.commit, exofind.sync.push, exofind.sync.pull, exofind.storage.operation, exofind.indexes.open.wait):

  • 10 ms
  • 50 ms
  • 250 ms
  • 1 s
  • 5 s
  • 15 s
  • 60 s
  • 5 min

Configure metrics behavior using the following environment variables:

SettingDefaultDescription
EXOFIND_METRICS_INDEX_ENABLEDtrueEnables per-index gauges (exofind.index.*). When false, only node-level meters and exofind.index.unhealthy remain active.
EXOFIND_METRICS_INDEX_INTERVAL30sInterval for rebuilding per-index metrics and scanning disk usage.
EXOFIND_METRICS_INDEX_SEARCH_HISTOGRAMfalseAdds the index tag to exofind.search, exofind.suggest, exofind.facet.values and exofind.explain, splitting each latency histogram per index.
EXOFIND_METRICS_HISTOGRAM_MODEsloHistogram bucket generation mode: slo, detailed, or none.
EXOFIND_METRICS_HTTP_MAX_URI_TAGS200Maximum number of distinct URI values allowed for http.server.requests before collapsing additional paths to UNKNOWN.

Exofind is built by Level Four AB and is available under the Apache License 2.0.