Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions benches/ore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,12 @@ use tokio::runtime::Runtime;
//
// **Hybrid ordered range** uses extractor ORDER BY (`ORDER BY
// eql_v2.ore_block_u64_8_256(val)`) matching the functional index expression —
// rows stream out of the index already sorted (Index Scan, no Sort node). The
// natural-form variant (`ORDER BY value`) is the §4 sort-key trap and was
// dropped from this bench in an earlier pass — its cost (Top-N Sort over the
// full post-WHERE bitmap) is documented in the guide already.
// rows stream out of the index already sorted (Index Scan, no Sort node).
//
// **Natural-form ordered range** uses column ORDER BY (`ORDER BY value`). The
// sort key doesn't match the functional index expression syntactically, so the
// plan keeps a residual Top-N Sort over the bitmap-scan output. The hybrid /
// natural pair documents the cost of taking the §4 sort-key shortcut.
static QUERY_TEMPLATES: &[(&str, i32, &str)] = &[
// ── Non-selective baselines (≈50% selectivity → Seq Scan + LIMIT) ──
(
Expand Down Expand Up @@ -98,13 +100,30 @@ static QUERY_TEMPLATES: &[(&str, i32, &str)] = &[
"range_highly_selective_gt_10",
),
// ── Hybrid ordered range (extractor in ORDER BY) ──
// Sort key matches the functional index expression syntactically, so rows
// stream out of the index already sorted — no Sort node in the plan.
(
"SELECT id,value::jsonb FROM {TABLE} \
WHERE value < $1 \
ORDER BY eql_v2.ore_block_u64_8_256(value) LIMIT 10",
5000,
"range_lt_hybrid_ordered_10",
),
// ── Natural-form ordered range (column in ORDER BY) ──
// Companion to the hybrid scenario above. Postgres can't structurally
// match `ORDER BY value` against the functional index expression, so the
// plan has a residual Top-N Sort over the bitmap-scan output. Post-EQL
// #218 each comparison in the sort is the inlined ORE-term path, so the
// residual cost is bounded by Sort + heap fetches rather than per-row
// plpgsql. The cost delta vs the hybrid form is what justifies (or
// doesn't) the §4 sort-key recommendation in the EQL perf guide.
(
"SELECT id,value::jsonb FROM {TABLE} \
WHERE value < $1 \
ORDER BY value LIMIT 10",
5000,
"range_lt_natural_ordered_10",
),
];

// Count-style selective scenarios — no LIMIT, so the planner must process
Expand Down
4 changes: 3 additions & 1 deletion report/BENCHMARK_REPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This report summarises the performance benchmarks for encrypted database operati
- [COMBO Queries](combo.md)
- [EXACT Queries](exact.md)
- [GROUP_BY Queries](group_by.md)
- [JSON Queries](json.md)
- [MATCH Queries](match.md)
- [ORE Queries](ore.md)

Expand Down Expand Up @@ -95,8 +96,9 @@ Per-query-type detail is broken out into separate pages — click into a scenari
| COMBO | `bloom_ore_order_limit`, `filtered_group_by`, `top_n_filtered_group_by` | 10,000, 100,000, 1,000,000, 10,000,000 | 98.05ms | [open](combo.md) |
| EXACT | `eql_cast`, `eql_hash` | 10,000, 100,000, 1,000,000, 10,000,000 | 454.21μs | [open](exact.md) |
| GROUP_BY | `low_cardinality_groups_encrypted`, `low_cardinality_groups_plaintext`, `top_n_groups_encrypted`, `top_n_groups_plaintext` | 10,000, 100,000, 1,000,000, 10,000,000 | 864.52ms | [open](group_by.md) |
| JSON | `contains`, `field_order` | 10,000, 100,000 | 2.426s | [open](json.md) |
| MATCH | `eql_bloom`, `eql_cast_firstname`, `eql_cast_lastname` | 10,000, 100,000, 1,000,000, 10,000,000 | 151.23ms | [open](match.md) |
| ORE | `range_gt_10`, `range_gt_100`, `range_highly_selective_gt_10`, `range_highly_selective_gt_count`, `range_lt_10`, `range_lt_100`, `range_lt_hybrid_ordered_10`, `range_selective_gt_100`, `range_selective_gt_count` | 10,000, 100,000, 1,000,000, 10,000,000 | 573.45ms | [open](ore.md) |
| ORE | `range_gt_10`, `range_gt_100`, `range_highly_selective_gt_10`, `range_highly_selective_gt_count`, `range_lt_10`, `range_lt_100`, `range_lt_hybrid_ordered_10`, `range_lt_natural_ordered_10`, `range_selective_gt_100`, `range_selective_gt_count` | 10,000, 100,000, 1,000,000, 10,000,000 | 573.45ms | [open](ore.md) |


---
Expand Down
386 changes: 325 additions & 61 deletions report/ore.md

Large diffs are not rendered by default.

Binary file modified report/query_ore_range_gt_100_chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified report/query_ore_range_gt_10_chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified report/query_ore_range_highly_selective_gt_10_chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified report/query_ore_range_highly_selective_gt_count_chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified report/query_ore_range_lt_100_chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified report/query_ore_range_lt_10_chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified report/query_ore_range_lt_hybrid_ordered_10_chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified report/query_ore_range_selective_gt_100_chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified report/query_ore_range_selective_gt_count_chart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions report_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,12 @@ def get_query_sql_and_param(self, query_type: str, query_name: str) -> Tuple[str
"WHERE value < $1 "
"ORDER BY eql_v2.ore_block_u64_8_256(value) LIMIT 10",
"5000"
),
"range_lt_natural_ordered_10": (
"SELECT id,value::jsonb FROM {TABLE} "
"WHERE value < $1 "
"ORDER BY value LIMIT 10",
"5000"
)
},
"GROUP_BY": {
Expand Down Expand Up @@ -472,6 +478,18 @@ def get_query_description(self, query_type: str, query_name: str) -> Tuple[str,
"out of the index already ordered — no Sort node. See §4 of the EQL "
"query-performance guide for the natural-form sort-key trap that this "
"shape avoids."
),
"range_lt_natural_ordered_10": (
"Ordered range query (natural form: column in ORDER BY)",
"Table: `integer_encrypted_{rows}` with Block-ORE-encrypted integer values. "
"Index: functional btree on `eql_v2.ore_block_u64_8_256(value)`. "
"Query: WHERE value < 5000 ORDER BY value LIMIT 10. The sort key doesn't "
"match the index expression, so the plan keeps a residual Top-N Sort over "
"the bitmap-scan output. Post-EQL #218 each comparison in the sort is the "
"inlined ORE-term path (no plpgsql dispatch per row), but the Sort cost "
"still scales with the size of the post-WHERE set. Companion to "
"`range_lt_hybrid_ordered_10`; the cost delta is the price of the §4 "
"sort-key shortcut."
)
},
"GROUP_BY": {
Expand Down
Loading