Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@binarycleric
Copy link
Owner

@binarycleric binarycleric commented Jun 25, 2025

This PR adds recall tracking functionality to monitor the quality of approximate vector indexes without the performance overhead of exact searches. It is disabled by default and must be explicity enabled by a user. Recall tracking can also be disabled in the event of bugs or performance impacts.

What it does

  • Tracks recall metrics using statistical sampling and distance-threshold counting
  • Provides SQL functions to query recall statistics with human-readable output
  • Configurable sampling rate to balance monitoring detail with performance impact
  • Memory-based storage - statistics persist until server restart

Configuration

SET pgvector.track_recall = on;
SET pgvector.recall_sample_rate = 50;  -- Sample every 50th query
SET pgvector.recall_max_scan_tuples = 10000 -- How many tuples to scan before exiting.

Usage

View recall summary with index names:

SELECT * FROM pg_vector_recall_summary();

Get raw statistics:

SELECT * FROM pg_vector_recall_stats();

Get statistics for a specific index:

SELECT pg_vector_recall_get(index_oid);

Reset stats for a specific index:

SELECT pg_vector_recall_reset(index_oid);

The recall estimation works by counting how many tuples in the table fall within the distance of the K-th result, providing a practical approximation of search quality without the cost of brute-force comparison.

@binarycleric binarycleric force-pushed the recall-metrics branch 2 times, most recently from 47b13bf to ddfad58 Compare June 25, 2025 21:04
Copy link

@dhagberg-sf dhagberg-sf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sanity check looks reasonable. I'd have to study internals to really dig in here on ownership/exception paths/etc.

README.md Outdated

Optionally, you can enable recall tracking to monitor the recall of your indexes.
These statistics are based on sampled queries and persist in memory, so they will
be lost on server restart.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this setting be changed while the server is running? Or does it require a restart to apply?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be changed at runtime. It doesn't require any shared libraries or modifying any settings that require a restart. I wanted to make sure it could be enabled/disabled easily and not incur any interuptions.

if (recall_context == NULL)
{
recall_context = AllocSetContextCreate(TopMemoryContext,
"Vector Recall Tracking",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: tab alignment weird in PR view, prob not in editor, not sure if project has standardized on space- or tab-indentation.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It uses tabs and I've run into some weird formatting issues as well.

void
VectorRecallTrackerInit(VectorRecallTracker *tracker)
{
tracker->query_value = (Datum) 0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like a pretty small set of operations, but is it worth having a null tracker and making this a noop if pg_track_recall is false? Same for UpdateDistance below?

bool found;

/* Don't proceed if recall tracking is disabled or no hash table */
if (!pgvector_track_recall || recall_stats_hash == NULL)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see - this is the real potential overhead and we noop when false.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'd rather keep the checks minimal and not litter them in every function. Seems better to noop during the expensive things.

if (isnull)
continue;

distanceDatum = FunctionCall2Coll(distance_proc, collation, tracker->query_value, value);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is a null distance_proc already handled in FunctionCall2Coll?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not. I'll add some safety checks and logging incase that happens. As best I can tell distance_proc shouldn't ever be null but I'd rather be safe.

* Track a vector query with safe recall estimation
*/
void
TrackVectorQuery(Relation index, VectorRecallTracker *tracker, FmgrInfo *distance_proc, Oid collation)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't done full Rust-style ownership checking in here but it does look like all resources allocated are properly closed/de-allocated in here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants