Renamed iterative_search to iterative_scan

This commit is contained in:
Andrew Kane
2024-10-27 14:02:22 -07:00
parent c5dd2af750
commit 857d716d9e
16 changed files with 100 additions and 100 deletions

View File

@@ -1,6 +1,6 @@
## 0.8.0 (unreleased) ## 0.8.0 (unreleased)
- Added support for iterative search - Added support for iterative index scans
- Added casts for arrays to `sparsevec` - Added casts for arrays to `sparsevec`
- Improved cost estimation - Improved cost estimation
- Improved performance of HNSW inserts and on-disk index builds - Improved performance of HNSW inserts and on-disk index builds

View File

@@ -451,31 +451,31 @@ Use [partitioning](https://www.postgresql.org/docs/current/ddl-partitioning.html
CREATE TABLE items (embedding vector(3), category_id int) PARTITION BY LIST(category_id); CREATE TABLE items (embedding vector(3), category_id int) PARTITION BY LIST(category_id);
``` ```
## Iterative Search ## Iterative Index Scans
*Unreleased* *Unreleased*
With approximate indexes, queries with filtering can return less results (due to post-filtering). With approximate indexes, queries with filtering can return less results (due to post-filtering).
Starting with 0.8.0, you can enable iterative search. If too few results from the initial index scan match the filters, the scan will resume until enough results are found (or it reaches `hnsw.max_search_tuples` or `ivfflat.max_probes`). This can significantly improve recall. Starting with 0.8.0, you can enable iterative index scans. If too few results from the initial scan match the filters, the scan will resume until enough results are found (or it reaches `hnsw.max_scan_tuples` or `ivfflat.max_probes`). This can significantly improve recall.
There are two modes for iterative search: strict and relaxed (due to the streaming nature of Postgres executor). There are two modes for iterative scans: strict and relaxed.
Strict ensures results are in the exact order by distance Strict ensures results are in the exact order by distance
```sql ```sql
SET hnsw.iterative_search = strict_order; SET hnsw.iterative_scan = strict_order;
``` ```
Relaxed allows results to be slightly out of order by distance, but provides better recall Relaxed allows results to be slightly out of order by distance, but provides better recall
```sql ```sql
SET hnsw.iterative_search = relaxed_order; SET hnsw.iterative_scan = relaxed_order;
# or # or
SET ivfflat.iterative_search = relaxed_order; SET ivfflat.iterative_scan = relaxed_order;
``` ```
Note: IVFFlat only supports relaxed order for iterative search Note: IVFFlat only supports relaxed ordering for iterative scans
With relaxed ordering, you can use a [materialized CTE](https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-CTE-MATERIALIZATION) to get strict ordering With relaxed ordering, you can use a [materialized CTE](https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-CTE-MATERIALIZATION) to get strict ordering
@@ -493,16 +493,16 @@ WITH filtered_results AS MATERIALIZED (
) SELECT * FROM filtered_results WHERE distance < 0.1 ORDER BY distance; ) SELECT * FROM filtered_results WHERE distance < 0.1 ORDER BY distance;
``` ```
### Iterative Options ### Iterative Scan Options
Since scanning a large portion of an approximate index is expensive, there are options to control when a scan ends Since scanning a large portion of an approximate index is expensive, there are options to control when a scan ends
#### HNSW #### HNSW
Specify the max number of tuples visited (20,000 by default) Specify the max number of tuples to visit (20,000 by default)
```sql ```sql
SET hnsw.max_search_tuples = 20000; SET hnsw.max_scan_tuples = 20000;
``` ```
Note: This is approximate and does not apply to the initial scan Note: This is approximate and does not apply to the initial scan
@@ -510,7 +510,7 @@ Note: This is approximate and does not apply to the initial scan
When increasing this, you may also need to increase the max amount of memory an iterative scan can use, which is a multiple of `work_mem` (2 by default) When increasing this, you may also need to increase the max amount of memory an iterative scan can use, which is a multiple of `work_mem` (2 by default)
```sql ```sql
SET hnsw.search_mem_multiplier = 4; SET hnsw.scan_mem_multiplier = 4;
``` ```
You can see when this is needed by enabling debug messages You can see when this is needed by enabling debug messages
@@ -523,7 +523,7 @@ which will show when a scan reaches the memory limit
```text ```text
DEBUG: hnsw index scan reached memory limit after 40000 tuples DEBUG: hnsw index scan reached memory limit after 40000 tuples
HINT: Increase hnsw.search_mem_multiplier to scan more tuples. HINT: Increase hnsw.scan_mem_multiplier to scan more tuples.
``` ```
#### IVFFlat #### IVFFlat

View File

@@ -18,17 +18,17 @@
#define MarkGUCPrefixReserved(x) EmitWarningsOnPlaceholders(x) #define MarkGUCPrefixReserved(x) EmitWarningsOnPlaceholders(x)
#endif #endif
static const struct config_enum_entry hnsw_iterative_search_options[] = { static const struct config_enum_entry hnsw_iterative_scan_options[] = {
{"off", HNSW_ITERATIVE_SEARCH_OFF, false}, {"off", HNSW_ITERATIVE_SCAN_OFF, false},
{"relaxed_order", HNSW_ITERATIVE_SEARCH_RELAXED, false}, {"relaxed_order", HNSW_ITERATIVE_SCAN_RELAXED, false},
{"strict_order", HNSW_ITERATIVE_SEARCH_STRICT, false}, {"strict_order", HNSW_ITERATIVE_SCAN_STRICT, false},
{NULL, 0, false} {NULL, 0, false}
}; };
int hnsw_ef_search; int hnsw_ef_search;
int hnsw_iterative_search; int hnsw_iterative_scan;
int hnsw_max_search_tuples; int hnsw_max_scan_tuples;
double hnsw_search_mem_multiplier; double hnsw_scan_mem_multiplier;
int hnsw_lock_tranche_id; int hnsw_lock_tranche_id;
static relopt_kind hnsw_relopt_kind; static relopt_kind hnsw_relopt_kind;
@@ -79,18 +79,18 @@ HnswInit(void)
"Valid range is 1..1000.", &hnsw_ef_search, "Valid range is 1..1000.", &hnsw_ef_search,
HNSW_DEFAULT_EF_SEARCH, HNSW_MIN_EF_SEARCH, HNSW_MAX_EF_SEARCH, PGC_USERSET, 0, NULL, NULL, NULL); HNSW_DEFAULT_EF_SEARCH, HNSW_MIN_EF_SEARCH, HNSW_MAX_EF_SEARCH, PGC_USERSET, 0, NULL, NULL, NULL);
DefineCustomEnumVariable("hnsw.iterative_search", "Sets the iterative search mode", DefineCustomEnumVariable("hnsw.iterative_scan", "Sets the mode for iterative scans",
NULL, &hnsw_iterative_search, NULL, &hnsw_iterative_scan,
HNSW_ITERATIVE_SEARCH_OFF, hnsw_iterative_search_options, PGC_USERSET, 0, NULL, NULL, NULL); HNSW_ITERATIVE_SCAN_OFF, hnsw_iterative_scan_options, PGC_USERSET, 0, NULL, NULL, NULL);
/* This is approximate and does not apply to the initial scan */ /* This is approximate and does not apply to the initial scan */
DefineCustomIntVariable("hnsw.max_search_tuples", "Sets the max number of candidates to visit for iterative search", DefineCustomIntVariable("hnsw.max_scan_tuples", "Sets the max number of tuples to visit for iterative scans",
NULL, &hnsw_max_search_tuples, NULL, &hnsw_max_scan_tuples,
20000, 1, INT_MAX, PGC_USERSET, 0, NULL, NULL, NULL); 20000, 1, INT_MAX, PGC_USERSET, 0, NULL, NULL, NULL);
/* Same range and default as hash_mem_multiplier */ /* Same range and default as hash_mem_multiplier */
DefineCustomRealVariable("hnsw.search_mem_multiplier", "Sets the multiple of work_mem to use for iterative search", DefineCustomRealVariable("hnsw.scan_mem_multiplier", "Sets the multiple of work_mem to use for iterative scans",
NULL, &hnsw_search_mem_multiplier, NULL, &hnsw_scan_mem_multiplier,
2, 1, 1000, PGC_USERSET, 0, NULL, NULL, NULL); 2, 1, 1000, PGC_USERSET, 0, NULL, NULL, NULL);
MarkGUCPrefixReserved("hnsw"); MarkGUCPrefixReserved("hnsw");

View File

@@ -109,17 +109,17 @@
/* Variables */ /* Variables */
extern int hnsw_ef_search; extern int hnsw_ef_search;
extern int hnsw_iterative_search; extern int hnsw_iterative_scan;
extern int hnsw_max_search_tuples; extern int hnsw_max_scan_tuples;
extern double hnsw_search_mem_multiplier; extern double hnsw_scan_mem_multiplier;
extern int hnsw_lock_tranche_id; extern int hnsw_lock_tranche_id;
typedef enum HnswIterativeSearchMode typedef enum HnswIterativeScanMode
{ {
HNSW_ITERATIVE_SEARCH_OFF, HNSW_ITERATIVE_SCAN_OFF,
HNSW_ITERATIVE_SEARCH_RELAXED, HNSW_ITERATIVE_SCAN_RELAXED,
HNSW_ITERATIVE_SEARCH_STRICT HNSW_ITERATIVE_SCAN_STRICT
} HnswIterativeSearchMode; } HnswIterativeScanMode;
typedef struct HnswElementData HnswElementData; typedef struct HnswElementData HnswElementData;
typedef struct HnswNeighborArray HnswNeighborArray; typedef struct HnswNeighborArray HnswNeighborArray;

View File

@@ -41,7 +41,7 @@ GetScanItems(IndexScanDesc scan, Datum value)
ep = w; ep = w;
} }
return HnswSearchLayer(base, q, ep, hnsw_ef_search, 0, index, support, m, false, NULL, &so->v, hnsw_iterative_search != HNSW_ITERATIVE_SEARCH_OFF ? &so->discarded : NULL, true, &so->tuples); return HnswSearchLayer(base, q, ep, hnsw_ef_search, 0, index, support, m, false, NULL, &so->v, hnsw_iterative_scan != HNSW_ITERATIVE_SCAN_OFF ? &so->discarded : NULL, true, &so->tuples);
} }
/* /*
@@ -138,7 +138,7 @@ hnswbeginscan(Relation index, int nkeys, int norderbys)
/* Set support functions */ /* Set support functions */
HnswInitSupport(&so->support, index); HnswInitSupport(&so->support, index);
so->maxMemory = (Size) (work_mem * 1024.0 * hnsw_search_mem_multiplier); so->maxMemory = (Size) (work_mem * 1024.0 * hnsw_scan_mem_multiplier);
scan->opaque = so; scan->opaque = so;
@@ -229,7 +229,7 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
if (list_length(so->w) == 0) if (list_length(so->w) == 0)
{ {
if (hnsw_iterative_search == HNSW_ITERATIVE_SEARCH_OFF) if (hnsw_iterative_scan == HNSW_ITERATIVE_SCAN_OFF)
break; break;
/* Empty index */ /* Empty index */
@@ -237,7 +237,7 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
break; break;
/* Reached max number of tuples */ /* Reached max number of tuples */
if (so->tuples >= hnsw_max_search_tuples) if (so->tuples >= hnsw_max_scan_tuples)
{ {
if (pairingheap_is_empty(so->discarded)) if (pairingheap_is_empty(so->discarded))
break; break;
@@ -252,7 +252,7 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
{ {
ereport(DEBUG1, ereport(DEBUG1,
(errmsg("hnsw index scan reached memory limit after " INT64_FORMAT " tuples", so->tuples), (errmsg("hnsw index scan reached memory limit after " INT64_FORMAT " tuples", so->tuples),
errhint("Increase hnsw.search_mem_multiplier to scan more tuples."))); errhint("Increase hnsw.scan_mem_multiplier to scan more tuples.")));
break; break;
} }
@@ -295,7 +295,7 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
so->w = list_delete_last(so->w); so->w = list_delete_last(so->w);
/* Mark memory as free for next iteration */ /* Mark memory as free for next iteration */
if (hnsw_iterative_search != HNSW_ITERATIVE_SEARCH_OFF) if (hnsw_iterative_scan != HNSW_ITERATIVE_SCAN_OFF)
{ {
pfree(element); pfree(element);
pfree(sc); pfree(sc);
@@ -306,7 +306,7 @@ hnswgettuple(IndexScanDesc scan, ScanDirection dir)
heaptid = &element->heaptids[--element->heaptidsLength]; heaptid = &element->heaptids[--element->heaptidsLength];
if (hnsw_iterative_search == HNSW_ITERATIVE_SEARCH_STRICT) if (hnsw_iterative_scan == HNSW_ITERATIVE_SCAN_STRICT)
{ {
if (sc->distance < so->previousDistance) if (sc->distance < so->previousDistance)
continue; continue;

View File

@@ -17,13 +17,13 @@
#endif #endif
int ivfflat_probes; int ivfflat_probes;
int ivfflat_iterative_search; int ivfflat_iterative_scan;
int ivfflat_max_probes; int ivfflat_max_probes;
static relopt_kind ivfflat_relopt_kind; static relopt_kind ivfflat_relopt_kind;
static const struct config_enum_entry ivfflat_iterative_search_options[] = { static const struct config_enum_entry ivfflat_iterative_scan_options[] = {
{"off", IVFFLAT_ITERATIVE_SEARCH_OFF, false}, {"off", IVFFLAT_ITERATIVE_SCAN_OFF, false},
{"relaxed_order", IVFFLAT_ITERATIVE_SEARCH_RELAXED, false}, {"relaxed_order", IVFFLAT_ITERATIVE_SCAN_RELAXED, false},
{NULL, 0, false} {NULL, 0, false}
}; };
@@ -41,12 +41,12 @@ IvfflatInit(void)
"Valid range is 1..lists.", &ivfflat_probes, "Valid range is 1..lists.", &ivfflat_probes,
IVFFLAT_DEFAULT_PROBES, IVFFLAT_MIN_LISTS, IVFFLAT_MAX_LISTS, PGC_USERSET, 0, NULL, NULL, NULL); IVFFLAT_DEFAULT_PROBES, IVFFLAT_MIN_LISTS, IVFFLAT_MAX_LISTS, PGC_USERSET, 0, NULL, NULL, NULL);
DefineCustomEnumVariable("ivfflat.iterative_search", "Sets the iterative search mode", DefineCustomEnumVariable("ivfflat.iterative_scan", "Sets the mode for iterative scans",
NULL, &ivfflat_iterative_search, NULL, &ivfflat_iterative_scan,
IVFFLAT_ITERATIVE_SEARCH_OFF, ivfflat_iterative_search_options, PGC_USERSET, 0, NULL, NULL, NULL); IVFFLAT_ITERATIVE_SCAN_OFF, ivfflat_iterative_scan_options, PGC_USERSET, 0, NULL, NULL, NULL);
/* If this is less than probes, probes is used */ /* If this is less than probes, probes is used */
DefineCustomIntVariable("ivfflat.max_probes", "Sets the max number of probes for iterative search", DefineCustomIntVariable("ivfflat.max_probes", "Sets the max number of probes for iterative scans",
NULL, &ivfflat_max_probes, NULL, &ivfflat_max_probes,
IVFFLAT_MAX_LISTS, IVFFLAT_MIN_LISTS, IVFFLAT_MAX_LISTS, PGC_USERSET, 0, NULL, NULL, NULL); IVFFLAT_MAX_LISTS, IVFFLAT_MIN_LISTS, IVFFLAT_MAX_LISTS, PGC_USERSET, 0, NULL, NULL, NULL);

View File

@@ -80,14 +80,14 @@
/* Variables */ /* Variables */
extern int ivfflat_probes; extern int ivfflat_probes;
extern int ivfflat_iterative_search; extern int ivfflat_iterative_scan;
extern int ivfflat_max_probes; extern int ivfflat_max_probes;
typedef enum IvfflatIterativeSearchMode typedef enum IvfflatIterativeScanMode
{ {
IVFFLAT_ITERATIVE_SEARCH_OFF, IVFFLAT_ITERATIVE_SCAN_OFF,
IVFFLAT_ITERATIVE_SEARCH_RELAXED IVFFLAT_ITERATIVE_SCAN_RELAXED
} IvfflatIterativeSearchMode; } IvfflatIterativeScanMode;
typedef struct VectorArrayData typedef struct VectorArrayData
{ {

View File

@@ -171,7 +171,7 @@ GetScanItems(IndexScanDesc scan, Datum value)
} }
} }
if (tuples < 100 && ivfflat_iterative_search == IVFFLAT_ITERATIVE_SEARCH_OFF) if (tuples < 100 && ivfflat_iterative_scan == IVFFLAT_ITERATIVE_SCAN_OFF)
ereport(DEBUG1, ereport(DEBUG1,
(errmsg("index scan found few tuples"), (errmsg("index scan found few tuples"),
errdetail("Index may have been created with little data."), errdetail("Index may have been created with little data."),
@@ -263,7 +263,7 @@ ivfflatbeginscan(Relation index, int nkeys, int norderbys)
/* Get lists and dimensions from metapage */ /* Get lists and dimensions from metapage */
IvfflatGetMetaPageInfo(index, &lists, &dimensions); IvfflatGetMetaPageInfo(index, &lists, &dimensions);
if (ivfflat_iterative_search != IVFFLAT_ITERATIVE_SEARCH_OFF) if (ivfflat_iterative_scan != IVFFLAT_ITERATIVE_SCAN_OFF)
maxProbes = Max(ivfflat_max_probes, probes); maxProbes = Max(ivfflat_max_probes, probes);
else else
maxProbes = probes; maxProbes = probes;

View File

@@ -104,7 +104,7 @@ DROP TABLE t;
CREATE TABLE t (val vector(3)); CREATE TABLE t (val vector(3));
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL); INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
CREATE INDEX ON t USING hnsw (val vector_l2_ops); CREATE INDEX ON t USING hnsw (val vector_l2_ops);
SET hnsw.iterative_search = strict_order; SET hnsw.iterative_scan = strict_order;
SET hnsw.ef_search = 1; SET hnsw.ef_search = 1;
SELECT * FROM t ORDER BY val <-> '[3,3,3]'; SELECT * FROM t ORDER BY val <-> '[3,3,3]';
val val
@@ -114,7 +114,7 @@ SELECT * FROM t ORDER BY val <-> '[3,3,3]';
[0,0,0] [0,0,0]
(3 rows) (3 rows)
SET hnsw.iterative_search = relaxed_order; SET hnsw.iterative_scan = relaxed_order;
SELECT * FROM t ORDER BY val <-> '[3,3,3]'; SELECT * FROM t ORDER BY val <-> '[3,3,3]';
val val
--------- ---------
@@ -123,7 +123,7 @@ SELECT * FROM t ORDER BY val <-> '[3,3,3]';
[0,0,0] [0,0,0]
(3 rows) (3 rows)
RESET hnsw.iterative_search; RESET hnsw.iterative_scan;
RESET hnsw.ef_search; RESET hnsw.ef_search;
DROP TABLE t; DROP TABLE t;
-- unlogged -- unlogged
@@ -165,21 +165,21 @@ SET hnsw.ef_search = 0;
ERROR: 0 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000) ERROR: 0 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000)
SET hnsw.ef_search = 1001; SET hnsw.ef_search = 1001;
ERROR: 1001 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000) ERROR: 1001 is outside the valid range for parameter "hnsw.ef_search" (1 .. 1000)
SHOW hnsw.iterative_search; SHOW hnsw.iterative_scan;
hnsw.iterative_search hnsw.iterative_scan
----------------------- ---------------------
off off
(1 row) (1 row)
SET hnsw.iterative_search = on; SET hnsw.iterative_scan = on;
ERROR: invalid value for parameter "hnsw.iterative_search": "on" ERROR: invalid value for parameter "hnsw.iterative_scan": "on"
HINT: Available values: off, relaxed_order, strict_order. HINT: Available values: off, relaxed_order, strict_order.
SHOW hnsw.max_search_tuples; SHOW hnsw.max_scan_tuples;
hnsw.max_search_tuples hnsw.max_scan_tuples
------------------------ ----------------------
20000 20000
(1 row) (1 row)
SET hnsw.max_search_tuples = 0; SET hnsw.max_scan_tuples = 0;
ERROR: 0 is outside the valid range for parameter "hnsw.max_search_tuples" (1 .. 2147483647) ERROR: 0 is outside the valid range for parameter "hnsw.max_scan_tuples" (1 .. 2147483647)
DROP TABLE t; DROP TABLE t;

View File

@@ -86,7 +86,7 @@ DROP TABLE t;
CREATE TABLE t (val vector(3)); CREATE TABLE t (val vector(3));
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL); INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 3); CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 3);
SET ivfflat.iterative_search = relaxed_order; SET ivfflat.iterative_scan = relaxed_order;
SELECT * FROM t ORDER BY val <-> '[3,3,3]'; SELECT * FROM t ORDER BY val <-> '[3,3,3]';
val val
--------- ---------
@@ -110,7 +110,7 @@ SELECT * FROM t ORDER BY val <-> '[3,3,3]';
[1,1,1] [1,1,1]
(2 rows) (2 rows)
RESET ivfflat.iterative_search; RESET ivfflat.iterative_scan;
RESET ivfflat.max_probes; RESET ivfflat.max_probes;
DROP TABLE t; DROP TABLE t;
-- unlogged -- unlogged
@@ -144,14 +144,14 @@ SET ivfflat.probes = 0;
ERROR: 0 is outside the valid range for parameter "ivfflat.probes" (1 .. 32768) ERROR: 0 is outside the valid range for parameter "ivfflat.probes" (1 .. 32768)
SET ivfflat.probes = 32769; SET ivfflat.probes = 32769;
ERROR: 32769 is outside the valid range for parameter "ivfflat.probes" (1 .. 32768) ERROR: 32769 is outside the valid range for parameter "ivfflat.probes" (1 .. 32768)
SHOW ivfflat.iterative_search; SHOW ivfflat.iterative_scan;
ivfflat.iterative_search ivfflat.iterative_scan
-------------------------- ------------------------
off off
(1 row) (1 row)
SET ivfflat.iterative_search = on; SET ivfflat.iterative_scan = on;
ERROR: invalid value for parameter "ivfflat.iterative_search": "on" ERROR: invalid value for parameter "ivfflat.iterative_scan": "on"
HINT: Available values: off, relaxed_order. HINT: Available values: off, relaxed_order.
SHOW ivfflat.max_probes; SHOW ivfflat.max_probes;
ivfflat.max_probes ivfflat.max_probes

View File

@@ -63,14 +63,14 @@ CREATE TABLE t (val vector(3));
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL); INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
CREATE INDEX ON t USING hnsw (val vector_l2_ops); CREATE INDEX ON t USING hnsw (val vector_l2_ops);
SET hnsw.iterative_search = strict_order; SET hnsw.iterative_scan = strict_order;
SET hnsw.ef_search = 1; SET hnsw.ef_search = 1;
SELECT * FROM t ORDER BY val <-> '[3,3,3]'; SELECT * FROM t ORDER BY val <-> '[3,3,3]';
SET hnsw.iterative_search = relaxed_order; SET hnsw.iterative_scan = relaxed_order;
SELECT * FROM t ORDER BY val <-> '[3,3,3]'; SELECT * FROM t ORDER BY val <-> '[3,3,3]';
RESET hnsw.iterative_search; RESET hnsw.iterative_scan;
RESET hnsw.ef_search; RESET hnsw.ef_search;
DROP TABLE t; DROP TABLE t;
@@ -98,12 +98,12 @@ SHOW hnsw.ef_search;
SET hnsw.ef_search = 0; SET hnsw.ef_search = 0;
SET hnsw.ef_search = 1001; SET hnsw.ef_search = 1001;
SHOW hnsw.iterative_search; SHOW hnsw.iterative_scan;
SET hnsw.iterative_search = on; SET hnsw.iterative_scan = on;
SHOW hnsw.max_search_tuples; SHOW hnsw.max_scan_tuples;
SET hnsw.max_search_tuples = 0; SET hnsw.max_scan_tuples = 0;
DROP TABLE t; DROP TABLE t;

View File

@@ -50,7 +50,7 @@ CREATE TABLE t (val vector(3));
INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL); INSERT INTO t (val) VALUES ('[0,0,0]'), ('[1,2,3]'), ('[1,1,1]'), (NULL);
CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 3); CREATE INDEX ON t USING ivfflat (val vector_l2_ops) WITH (lists = 3);
SET ivfflat.iterative_search = relaxed_order; SET ivfflat.iterative_scan = relaxed_order;
SELECT * FROM t ORDER BY val <-> '[3,3,3]'; SELECT * FROM t ORDER BY val <-> '[3,3,3]';
SET ivfflat.max_probes = 1; SET ivfflat.max_probes = 1;
@@ -59,7 +59,7 @@ SELECT * FROM t ORDER BY val <-> '[3,3,3]';
SET ivfflat.max_probes = 2; SET ivfflat.max_probes = 2;
SELECT * FROM t ORDER BY val <-> '[3,3,3]'; SELECT * FROM t ORDER BY val <-> '[3,3,3]';
RESET ivfflat.iterative_search; RESET ivfflat.iterative_scan;
RESET ivfflat.max_probes; RESET ivfflat.max_probes;
DROP TABLE t; DROP TABLE t;
@@ -84,9 +84,9 @@ SHOW ivfflat.probes;
SET ivfflat.probes = 0; SET ivfflat.probes = 0;
SET ivfflat.probes = 32769; SET ivfflat.probes = 32769;
SHOW ivfflat.iterative_search; SHOW ivfflat.iterative_scan;
SET ivfflat.iterative_search = on; SET ivfflat.iterative_scan = on;
SHOW ivfflat.max_probes; SHOW ivfflat.max_probes;

View File

@@ -23,7 +23,7 @@ $node->safe_psql("postgres", "CREATE INDEX ON tst USING ivfflat (v vector_l2_ops
my $count = $node->safe_psql("postgres", qq( my $count = $node->safe_psql("postgres", qq(
SET enable_seqscan = off; SET enable_seqscan = off;
SET ivfflat.probes = 10; SET ivfflat.probes = 10;
SET ivfflat.iterative_search = relaxed_order; SET ivfflat.iterative_scan = relaxed_order;
SELECT COUNT(*) FROM (SELECT v FROM tst WHERE i % 10000 = 0 ORDER BY v <-> (SELECT v FROM tst LIMIT 1) LIMIT 11) t; SELECT COUNT(*) FROM (SELECT v FROM tst WHERE i % 10000 = 0 ORDER BY v <-> (SELECT v FROM tst LIMIT 1) LIMIT 11) t;
)); ));
is($count, 10); is($count, 10);
@@ -39,7 +39,7 @@ foreach ((30, 50, 70))
$count = $node->safe_psql("postgres", qq( $count = $node->safe_psql("postgres", qq(
SET enable_seqscan = off; SET enable_seqscan = off;
SET ivfflat.probes = 10; SET ivfflat.probes = 10;
SET ivfflat.iterative_search = relaxed_order; SET ivfflat.iterative_scan = relaxed_order;
SET ivfflat.max_probes = $max_probes; SET ivfflat.max_probes = $max_probes;
SELECT COUNT(*) FROM (SELECT v FROM tst WHERE i % 10000 = 0 ORDER BY v <-> (SELECT v FROM tst WHERE i = $i) LIMIT 11) t; SELECT COUNT(*) FROM (SELECT v FROM tst WHERE i % 10000 = 0 ORDER BY v <-> (SELECT v FROM tst WHERE i = $i) LIMIT 11) t;
)); ));

View File

@@ -19,7 +19,7 @@ sub test_recall
my $explain = $node->safe_psql("postgres", qq( my $explain = $node->safe_psql("postgres", qq(
SET enable_seqscan = off; SET enable_seqscan = off;
SET ivfflat.probes = $probes; SET ivfflat.probes = $probes;
SET ivfflat.iterative_search = relaxed_order; SET ivfflat.iterative_scan = relaxed_order;
EXPLAIN ANALYZE SELECT i FROM tst WHERE i % $c = 0 ORDER BY v $operator '$queries[0]' LIMIT $limit; EXPLAIN ANALYZE SELECT i FROM tst WHERE i % $c = 0 ORDER BY v $operator '$queries[0]' LIMIT $limit;
)); ));
like($explain, qr/Index Scan using idx on tst/); like($explain, qr/Index Scan using idx on tst/);
@@ -29,7 +29,7 @@ sub test_recall
my $actual = $node->safe_psql("postgres", qq( my $actual = $node->safe_psql("postgres", qq(
SET enable_seqscan = off; SET enable_seqscan = off;
SET ivfflat.probes = $probes; SET ivfflat.probes = $probes;
SET ivfflat.iterative_search = relaxed_order; SET ivfflat.iterative_scan = relaxed_order;
SELECT i FROM tst WHERE i % $c = 0 ORDER BY v $operator '$queries[$i]' LIMIT $limit; SELECT i FROM tst WHERE i % $c = 0 ORDER BY v $operator '$queries[$i]' LIMIT $limit;
)); ));
my @actual_ids = split("\n", $actual); my @actual_ids = split("\n", $actual);

View File

@@ -26,8 +26,8 @@ $node->safe_psql("postgres", qq(
my $count = $node->safe_psql("postgres", qq( my $count = $node->safe_psql("postgres", qq(
SET enable_seqscan = off; SET enable_seqscan = off;
SET hnsw.iterative_search = relaxed_order; SET hnsw.iterative_scan = relaxed_order;
SET hnsw.max_search_tuples = 100000; SET hnsw.max_scan_tuples = 100000;
SELECT COUNT(*) FROM (SELECT v FROM tst WHERE i % 10000 = 0 ORDER BY v <-> (SELECT v FROM tst LIMIT 1) LIMIT 11) t; SELECT COUNT(*) FROM (SELECT v FROM tst WHERE i % 10000 = 0 ORDER BY v <-> (SELECT v FROM tst LIMIT 1) LIMIT 11) t;
)); ));
is($count, 10); is($count, 10);
@@ -42,8 +42,8 @@ foreach ((30000, 50000, 70000))
{ {
$count = $node->safe_psql("postgres", qq( $count = $node->safe_psql("postgres", qq(
SET enable_seqscan = off; SET enable_seqscan = off;
SET hnsw.iterative_search = relaxed_order; SET hnsw.iterative_scan = relaxed_order;
SET hnsw.max_search_tuples = $max_tuples; SET hnsw.max_scan_tuples = $max_tuples;
SELECT COUNT(*) FROM (SELECT v FROM tst WHERE i % 10000 = 0 ORDER BY v <-> (SELECT v FROM tst WHERE i = $i) LIMIT 11) t; SELECT COUNT(*) FROM (SELECT v FROM tst WHERE i % 10000 = 0 ORDER BY v <-> (SELECT v FROM tst WHERE i = $i) LIMIT 11) t;
)); ));
$sum += $count; $sum += $count;
@@ -56,10 +56,10 @@ foreach ((30000, 50000, 70000))
my ($ret, $stdout, $stderr) = $node->psql("postgres", qq( my ($ret, $stdout, $stderr) = $node->psql("postgres", qq(
SET enable_seqscan = off; SET enable_seqscan = off;
SET hnsw.iterative_search = relaxed_order; SET hnsw.iterative_scan = relaxed_order;
SET client_min_messages = debug1; SET client_min_messages = debug1;
SET work_mem = '1MB'; SET work_mem = '1MB';
SET hnsw.search_mem_multiplier = 1; SET hnsw.scan_mem_multiplier = 1;
SELECT COUNT(*) FROM (SELECT v FROM tst WHERE i % 10000 = 0 ORDER BY v <-> (SELECT v FROM tst LIMIT 1) LIMIT 11) t; SELECT COUNT(*) FROM (SELECT v FROM tst WHERE i % 10000 = 0 ORDER BY v <-> (SELECT v FROM tst LIMIT 1) LIMIT 11) t;
)); ));
like($stderr, qr/hnsw index scan reached memory limit after \d+ tuples/); like($stderr, qr/hnsw index scan reached memory limit after \d+ tuples/);

View File

@@ -21,7 +21,7 @@ sub test_recall
my $explain = $node->safe_psql("postgres", qq( my $explain = $node->safe_psql("postgres", qq(
SET enable_seqscan = off; SET enable_seqscan = off;
SET hnsw.ef_search = $ef_search; SET hnsw.ef_search = $ef_search;
SET hnsw.iterative_search = $mode; SET hnsw.iterative_scan = $mode;
EXPLAIN ANALYZE SELECT i FROM tst WHERE i % $c = 0 ORDER BY v $operator '$queries[0]' LIMIT $limit; EXPLAIN ANALYZE SELECT i FROM tst WHERE i % $c = 0 ORDER BY v $operator '$queries[0]' LIMIT $limit;
)); ));
like($explain, qr/Index Scan using idx on tst/); like($explain, qr/Index Scan using idx on tst/);
@@ -31,7 +31,7 @@ sub test_recall
my $actual = $node->safe_psql("postgres", qq( my $actual = $node->safe_psql("postgres", qq(
SET enable_seqscan = off; SET enable_seqscan = off;
SET hnsw.ef_search = $ef_search; SET hnsw.ef_search = $ef_search;
SET hnsw.iterative_search = $mode; SET hnsw.iterative_scan = $mode;
SELECT i FROM tst WHERE i % $c = 0 ORDER BY v $operator '$queries[$i]' LIMIT $limit; SELECT i FROM tst WHERE i % $c = 0 ORDER BY v $operator '$queries[$i]' LIMIT $limit;
)); ));
my @actual_ids = split("\n", $actual); my @actual_ids = split("\n", $actual);