Skip to content

Commit d182c25

Browse files
authored
fix: restore KLL UDF impl mode rendering (#297)
1 parent 8bf126a commit d182c25

5 files changed

Lines changed: 155 additions & 37 deletions

File tree

‎asap-summary-ingest/run_arroyosketch.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,10 @@ def main(args):
952952
parameters["impl_mode"] = getattr(
953953
args, "sketch_cmwh_impl", "legacy"
954954
).capitalize()
955+
elif agg_function in ("datasketcheskll_", "hydrakll_"):
956+
parameters["impl_mode"] = getattr(
957+
args, "sketch_kll_impl", "sketchlib"
958+
).capitalize()
955959

956960
sql_queries.append(sql_query)
957961
# if not is_labels_accumulator:

‎asap-summary-ingest/templates/udfs/datasketcheskll_.rs.j2‎

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
11
/*
22
[dependencies]
3+
dsrs = { git = "https://github.com/ProjectASAP/datasketches-rs", rev = "d748ec75c80fff21f7b24897244dd1c895df2e9a" }
34
asap_sketchlib = { git = "https://github.com/ProjectASAP/asap_sketchlib" }
45
arroyo-udf-plugin = "0.1"
56
rmp-serde = "1.1"
67
serde = { version = "1.0", features = ["derive"] }
78
*/
89

910
use arroyo_udf_plugin::udf;
11+
use dsrs::KllDoubleSketch;
1012
use serde::{Deserialize, Serialize};
1113
use asap_sketchlib::KLL;
1214

1315
const DEFAULT_K: u16 = {{ k }};
1416

17+
enum ImplMode {
18+
Legacy,
19+
Sketchlib,
20+
}
21+
22+
{% set _impl_mode = impl_mode | default("Sketchlib") %}
23+
const IMPL_MODE: ImplMode = ImplMode::{% if _impl_mode == "Legacy" or _impl_mode == "Sketchlib" %}{{ _impl_mode }}{% else %}Sketchlib{% endif %};
24+
25+
fn use_sketchlib_for_kll() -> bool {
26+
matches!(IMPL_MODE, ImplMode::Sketchlib)
27+
}
28+
1529
#[derive(Serialize, Deserialize)]
1630
struct KllSketchData {
1731
k: u16,
@@ -20,11 +34,19 @@ struct KllSketchData {
2034

2135
#[udf]
2236
fn datasketcheskll_(values: Vec<f64>) -> Option<Vec<u8>> {
23-
let mut sketch = KLL::init_kll(DEFAULT_K as i32);
24-
for &value in &values {
25-
sketch.update(&value);
26-
}
27-
let sketch_bytes = sketch.serialize_to_bytes().ok()?;
37+
let sketch_bytes = if use_sketchlib_for_kll() {
38+
let mut sketch = KLL::init_kll(DEFAULT_K as i32);
39+
for &value in &values {
40+
sketch.update(&value);
41+
}
42+
sketch.serialize_to_bytes().ok()?
43+
} else {
44+
let mut sketch = KllDoubleSketch::with_k(DEFAULT_K);
45+
for &value in &values {
46+
sketch.update(value);
47+
}
48+
sketch.serialize().as_ref().to_vec()
49+
};
2850
let serialized = KllSketchData {
2951
k: DEFAULT_K,
3052
sketch_bytes,

‎asap-summary-ingest/templates/udfs/hydrakll_.rs.j2‎

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
[dependencies]
3+
dsrs = { git = "https://github.com/ProjectASAP/datasketches-rs", rev = "d748ec75c80fff21f7b24897244dd1c895df2e9a" }
34
asap_sketchlib = { git = "https://github.com/ProjectASAP/asap_sketchlib" }
45
arroyo-udf-plugin = "0.1"
56
rmp-serde = "1.1"
@@ -8,11 +9,24 @@ xxhash-rust = { version = "0.8", features = ["xxh32"] }
89
*/
910

1011
use arroyo_udf_plugin::udf;
12+
use dsrs::KllDoubleSketch;
1113
use rmp_serde::Serializer;
1214
use serde::{Deserialize, Serialize};
1315
use asap_sketchlib::KLL;
1416
use xxhash_rust::xxh32::xxh32;
1517

18+
enum ImplMode {
19+
Legacy,
20+
Sketchlib,
21+
}
22+
23+
{% set _impl_mode = impl_mode | default("Sketchlib") %}
24+
const IMPL_MODE: ImplMode = ImplMode::{% if _impl_mode == "Legacy" or _impl_mode == "Sketchlib" %}{{ _impl_mode }}{% else %}Sketchlib{% endif %};
25+
26+
fn use_sketchlib_for_kll() -> bool {
27+
matches!(IMPL_MODE, ImplMode::Sketchlib)
28+
}
29+
1630
const ROW_NUM: usize = {{ row_num }};
1731
const COL_NUM: usize = {{ col_num }};
1832
const DEFAULT_K: u16 = {{ k }};
@@ -32,40 +46,76 @@ struct HydraKllSketchData {
3246

3347
#[udf]
3448
fn hydrakll_(keys: Vec<&str>, values: Vec<f64>) -> Option<Vec<u8>> {
35-
let mut sketches: Vec<Vec<KLL>> = (0..ROW_NUM)
36-
.map(|_| {
37-
(0..COL_NUM)
38-
.map(|_| KLL::init_kll(DEFAULT_K as i32))
39-
.collect()
40-
})
41-
.collect();
49+
let sketch_data: Vec<Vec<KllSketchData>> = if use_sketchlib_for_kll() {
50+
let mut sketches: Vec<Vec<KLL>> = (0..ROW_NUM)
51+
.map(|_| {
52+
(0..COL_NUM)
53+
.map(|_| KLL::init_kll(DEFAULT_K as i32))
54+
.collect()
55+
})
56+
.collect();
4257

43-
for (i, &key) in keys.iter().enumerate() {
44-
if i >= values.len() {
45-
break;
58+
for (i, &key) in keys.iter().enumerate() {
59+
if i >= values.len() {
60+
break;
61+
}
62+
let key_bytes = key.as_bytes();
63+
for row in 0..ROW_NUM {
64+
let hash_value = xxh32(key_bytes, row as u32);
65+
let col_index = (hash_value as usize) % COL_NUM;
66+
sketches[row][col_index].update(&values[i]);
67+
}
4668
}
47-
let key_bytes = key.as_bytes();
48-
for row in 0..ROW_NUM {
49-
let hash_value = xxh32(key_bytes, row as u32);
50-
let col_index = (hash_value as usize) % COL_NUM;
51-
sketches[row][col_index].update(&values[i]);
69+
70+
sketches
71+
.iter()
72+
.map(|row| {
73+
row.iter()
74+
.map(|sketch| {
75+
let sketch_bytes = sketch.serialize_to_bytes().ok()?;
76+
Some(KllSketchData {
77+
k: DEFAULT_K,
78+
sketch_bytes,
79+
})
80+
})
81+
.collect::<Option<Vec<_>>>()
82+
})
83+
.collect::<Option<Vec<_>>>()?
84+
} else {
85+
let mut sketches: Vec<Vec<KllDoubleSketch>> = (0..ROW_NUM)
86+
.map(|_| {
87+
(0..COL_NUM)
88+
.map(|_| KllDoubleSketch::with_k(DEFAULT_K))
89+
.collect()
90+
})
91+
.collect();
92+
93+
for (i, &key) in keys.iter().enumerate() {
94+
if i >= values.len() {
95+
break;
96+
}
97+
let key_bytes = key.as_bytes();
98+
for row in 0..ROW_NUM {
99+
let hash_value = xxh32(key_bytes, row as u32);
100+
let col_index = (hash_value as usize) % COL_NUM;
101+
sketches[row][col_index].update(values[i]);
102+
}
52103
}
53-
}
54104

55-
let sketch_data: Vec<Vec<KllSketchData>> = sketches
56-
.iter()
57-
.map(|row| {
58-
row.iter()
59-
.map(|sketch| {
60-
let sketch_bytes = sketch.serialize_to_bytes().ok()?;
61-
Some(KllSketchData {
62-
k: DEFAULT_K,
63-
sketch_bytes,
105+
sketches
106+
.iter()
107+
.map(|row| {
108+
row.iter()
109+
.map(|sketch| {
110+
Some(KllSketchData {
111+
k: DEFAULT_K,
112+
sketch_bytes: sketch.serialize().as_ref().to_vec(),
113+
})
64114
})
65-
})
66-
.collect::<Option<Vec<_>>>()
67-
})
68-
.collect::<Option<Vec<_>>>()?;
115+
.collect::<Option<Vec<_>>>()
116+
})
117+
.collect::<Option<Vec<_>>>()?
118+
};
69119

70120
let hydra_data = HydraKllSketchData {
71121
row_num: ROW_NUM,

‎asap-summary-ingest/tests/test_integration.py‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,5 +430,48 @@ def test_parse_promql_config_file(self):
430430
agg_configs[0].validate(metric_config, query_language="promql")
431431

432432

433+
class TestKllUdfTemplates:
434+
"""Tests for KLL UDF template rendering."""
435+
436+
@pytest.fixture
437+
def udf_template_dir(self):
438+
"""Load the UDF template directory."""
439+
return os.path.join(
440+
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
441+
"templates",
442+
"udfs",
443+
)
444+
445+
def test_template_variables_ignore_local_jinja_assignments(self):
446+
"""Test that local Jinja variables are not treated as required params."""
447+
template_source = """
448+
{% set _impl_mode = impl_mode | default("Sketchlib") %}
449+
{{ _impl_mode }} {{ k }}
450+
"""
451+
template_vars = jinja_utils.get_template_variables(template_source)
452+
453+
assert template_vars == {"impl_mode", "k"}
454+
455+
def test_datasketches_kll_template_renders_legacy_mode(self, udf_template_dir):
456+
"""Test rendering the single KLL UDF with the legacy backend."""
457+
template = jinja_utils.load_template(udf_template_dir, "datasketcheskll_.rs.j2")
458+
rendered = template.render(k=200, impl_mode="Legacy")
459+
460+
assert "const IMPL_MODE: ImplMode = ImplMode::Legacy;" in rendered
461+
assert "KllDoubleSketch::with_k(DEFAULT_K)" in rendered
462+
assert "asap_sketchlib" in rendered
463+
assert "{{" not in rendered
464+
465+
def test_hydra_kll_template_renders_sketchlib_mode(self, udf_template_dir):
466+
"""Test rendering the Hydra KLL UDF with the sketchlib backend."""
467+
template = jinja_utils.load_template(udf_template_dir, "hydrakll_.rs.j2")
468+
rendered = template.render(row_num=3, col_num=128, k=200, impl_mode="Sketchlib")
469+
470+
assert "const IMPL_MODE: ImplMode = ImplMode::Sketchlib;" in rendered
471+
assert "KLL::init_kll(DEFAULT_K as i32)" in rendered
472+
assert "KllDoubleSketch::with_k(DEFAULT_K)" in rendered
473+
assert "{{" not in rendered
474+
475+
433476
if __name__ == "__main__":
434477
pytest.main([__file__, "-v"])

‎asap-summary-ingest/utils/jinja_utils.py‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from jinja2 import Environment, FileSystemLoader, nodes
1+
from jinja2 import Environment, FileSystemLoader, meta
22

33

44
def load_template(template_dir, template_name):
@@ -23,5 +23,4 @@ def get_template_variables(template_source, environment=None):
2323
environment = Environment()
2424

2525
ast = environment.parse(template_source)
26-
template_vars = ast.find_all(nodes.Name)
27-
return {var.name for var in template_vars if var.ctx == "load"}
26+
return meta.find_undeclared_variables(ast)

0 commit comments

Comments
 (0)