wedhornsipgate created an issue (kamailio/kamailio#4426)
**Description:**
We're missing a KEMI function in the htable module that provides the same functionality as the PV function `$shtcn(table=>pattern)` to count the number of keys in an htable that match a given pattern (prefix/suffix/regex). The goal is to let KEMI scripts perform pattern counts without constructing dynamic PV expressions, which currently causes unbounded pv_cache growth in PKG memory.
**Use case / motivation:**
We implemented rolling-window rate limiting by writing short-lived, per-call entries into an htable and then counting keys that match a number-derived pattern. Using $shtcn() works well for the logic, but in KEMI (JS/TypeScript) we must call it via pv.gete("$shtcn(...)"). Because the PV string varies per INVITE (different pattern/key each time), these dynamic PV expressions accumulate in the PV cache, growing PKG/private memory over time.
We’d like a native KEMI function to perform the same count/match so we can avoid the PV layer entirely.
**Environment**
Kamailio: 5.8.6 on Debian 12
Scripting: KEMI (JavaScript/TypeScript)
Symptom: steady PKG/private memory increase over ~24h under load when using pv.gete("$shtcn(...)"). Disabling the $shtcn() call stops the growth.
Related log line: the one discussed in [#3440](https://github.com/kamailio/kamailio/issues/3440) (“pv cache … pkg memory may get filled with pv declarations”).
Current line in our script:
```if (ksr.pv.gete(`$shtcn(${htable_count}=>%~${number})`) > threshold) { ... }```
**Current workaround**
Restarting the Kamailio every day or disabling rate limit on instances with KEMI.
**Idea for API**
Minimal, PV-parity:
``` // KEMI export int shtcn_match(str table, str pattern, int mode /* 0=exact, 1=prefix, 2=suffix, 3=regex */); ```
JS example:
`const n = KSR.htable.shtcn_match("from_number_count", number, 3); // regex`
More explicit helpers (avoids integer modes):
``` KSR.htable.shtcn_exact(table, key) -> int KSR.htable.shtcn_prefix(table, prefix) -> int KSR.htable.shtcn_suffix(table, suffix) -> int KSR.htable.shtcn_regex(table, pattern) -> int ```
**Behavioral notes / expectations**
Should mirror $shtcn() semantics where reasonable (esp. regex behavior), but returning an integer directly to KEMI.
Must not allocate PV expressions or interact with pv_cache.
**Why this belongs in htable KEMI**
$shtcn() is already offered at the PV level; parity in KEMI keeps feature consistency across scripting engines.
It enables memory-safe implementations of rolling-window rate limits and similar patterns (without touching PV).
**Alternatives considered**
Replace pattern counting with per-bucket counters (sht_inc), which leads to other problems like increment/decrement handling and locking.
Use ratelimit/pike modules—useful for IP/method limits, but our use case is PAI/From/To based and currently hinges on pattern counts.
**Acceptance criteria**
A KEMI function exists to count matching keys in an htable without PV parsing.
Verified that repeated calls with dynamic patterns do not grow PV cache / PKG memory.
Basic tests for exact/prefix/suffix/regex modes (and skip_expired if implemented).
Thank you!
This would let KEMI users keep $shtcn()-style logic with the same performance characteristics, but without the PV-cache side effects.
xkaraman left a comment (kamailio/kamailio#4426)
Hey @wedhornsipgate,
I have implement these extra KEMI functions in https://github.com/xkaraman/kamailio/tree/htable_kemi. You can have a look and try it your self and give any feedback.
Here is how i am using it with KEMI Python engine ``` python KSR.htable.sht_setxs("ipban", "pattern1", "value",50) KSR.htable.sht_setxs("ipban", "pattern2", "value1",50) KSR.htable.sht_setxs("ipban", "nopatter23", "value52",50) KSR.htable.sht_setxs("ipban", "abcd2", "empty1",50) # KSR.htable.sht_print()
exact="pattern1" count_exact = KSR.htable.shtcn_match("ipban", exact, 0) KSR.info("Exact match count for pattern [%s]: %d\n" % (exact, count_exact)) count_exact = KSR.htable.shtcn_exact("ipban", exact) KSR.info("Exact match count (exact) for pattern [%s]: %d\n" % (exact, count_exact))
startwith="pattern" count_startwith = KSR.htable.shtcn_match("ipban", startwith, 1) KSR.info("Startwith match count for pattern [%s]: %d\n" % (startwith, count_startwith)) count_startwith = KSR.htable.shtcn_prefix("ipban", startwith) KSR.info("Startwith match count (prefix) for pattern [%s]: %d\n" % (startwith, count_startwith))
endwith="2" count_endwith = KSR.htable.shtcn_match("ipban", endwith, 2) KSR.info("Endwith match count for pattern [%s]: %d\n" % (endwith, count_endwith)) count_endwith = KSR.htable.shtcn_suffix("ipban", endwith) KSR.info("Endwith match count (suffix) for pattern [%s]: %d\n" % (endwith, count_endwith))
regex="^pat.*2$" count_regex = KSR.htable.shtcn_match("ipban", regex, 3) KSR.info("Regex match count for pattern [%s]: %d\n" % (regex, count_regex)) count_regex = KSR.htable.shtcn_regex("ipban", regex) KSR.info("Regex match count (regex) for pattern [%s]: %d\n" % (regex, count_regex))
```
and the respective output logs:
``` python Nov 5 11:00:18 app01 sbin/kamailio[3338583]: INFO: {1 2426 REGISTER u30SqMAHWR} <core> [core/kemi.c:109]: sr_kemi_core_info(): ===== request - from kamailio python script Nov 5 11:00:18 app01 sbin/kamailio[3338583]: INFO: {1 2426 REGISTER u30SqMAHWR} <core> [core/kemi.c:109]: sr_kemi_core_info(): Exact match count for pattern [pattern1]: 1 Nov 5 11:00:18 app01 sbin/kamailio[3338583]: INFO: {1 2426 REGISTER u30SqMAHWR} <core> [core/kemi.c:109]: sr_kemi_core_info(): Exact match count (exact) for pattern [pattern1]: 1 Nov 5 11:00:18 app01 sbin/kamailio[3338583]: INFO: {1 2426 REGISTER u30SqMAHWR} <core> [core/kemi.c:109]: sr_kemi_core_info(): Startwith match count for pattern [pattern]: 2 Nov 5 11:00:18 app01 sbin/kamailio[3338583]: INFO: {1 2426 REGISTER u30SqMAHWR} <core> [core/kemi.c:109]: sr_kemi_core_info(): Startwith match count (prefix) for pattern [pattern]: 2 Nov 5 11:00:18 app01 sbin/kamailio[3338583]: INFO: {1 2426 REGISTER u30SqMAHWR} <core> [core/kemi.c:109]: sr_kemi_core_info(): Endwith match count for pattern [2]: 2 Nov 5 11:00:18 app01 sbin/kamailio[3338583]: INFO: {1 2426 REGISTER u30SqMAHWR} <core> [core/kemi.c:109]: sr_kemi_core_info(): Endwith match count (suffix) for pattern [2]: 2 Nov 5 11:00:18 app01 sbin/kamailio[3338583]: INFO: {1 2426 REGISTER u30SqMAHWR} <core> [core/kemi.c:109]: sr_kemi_core_info(): Regex match count for pattern [^pat.*2$]: 1 Nov 5 11:00:18 app01 sbin/kamailio[3338583]: INFO: {1 2426 REGISTER u30SqMAHWR} <core> [core/kemi.c:109]: sr_kemi_core_info(): Regex match count (regex) for pattern [^pat.*2$]: 1
```
boettner left a comment (kamailio/kamailio#4426)
Hey @xkaraman, thanks a lot the feature. We tested it, and it worked perfectly for our use case.
We did not integrate it in production yet so we cannot rule out high memory consumption over time (like using pv.get($shtcn, ...).
Closed #4426 as completed.
miconda left a comment (kamailio/kamailio#4426)
The function was added to git devel branch, but with a different name and prototype: `KSR.htable.sht_cn(htable, op, value)`.