Skip to content

Commit 9a89375

Browse files
committed
cachedb_local: raise event on cache entry expiry
Add E_CACHEDB_LOCAL_EXPIRED event support, allowing users to be notified via event_route when cached entries expire. The event is raised during the periodic cleanup timer (cache_clean_period) with the following parameters: - key: the expired cache key - value: the expired cache value - collection: the cache collection name Example usage in the OpenSIPS script: event_route[E_CACHEDB_LOCAL_EXPIRED] { xlog("expired: $param(key) = $param(value)\n"); } The event is raised outside the per-bucket hash table lock to avoid deadlocks when the event_route handler accesses the cache. Expired entry data is copied to pkg memory before lock release and freed after the event is dispatched. Uses the standard EVI (Event Interface) framework following the same pattern as usrloc and dialog modules. An evi_probe_event() check avoids parameter setup overhead when no subscribers exist. Closes #3735
1 parent 5b5ce1f commit 9a89375

4 files changed

Lines changed: 205 additions & 0 deletions

File tree

modules/cachedb_local/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,35 @@ opensips-cli -x mi cachedb_local:fetch_chunk "keyprefix*" collection
350350
```
351351

352352

353+
### Exported Events
354+
355+
356+
#### E_CACHEDB_LOCAL_EXPIRED
357+
358+
359+
This event is raised when a cached entry expires and is removed
360+
during the periodic cleanup (controlled by *cache_clean_period*).
361+
362+
363+
Parameters:
364+
365+
366+
- *key* - The key of the expired cache entry.
367+
- *value* - The value of the expired cache entry.
368+
- *collection* - The name of the cache collection.
369+
370+
371+
Example usage:
372+
373+
374+
```bash
375+
event_route[E_CACHEDB_LOCAL_EXPIRED] {
376+
xlog("cache expired: $param(key) = $param(value) "
377+
"in collection $param(collection)\n");
378+
}
379+
```
380+
381+
353382
## Frequently Asked Questions
354383

355384

modules/cachedb_local/cachedb_local.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
#include "cachedb_local.h"
4444
#include "cachedb_local_replication.h"
45+
#include "cachedb_local_evi.h"
4546
#include "hash.h"
4647

4748
#include "../../mem/rpm_mem.h"
@@ -743,6 +744,11 @@ static int mod_init(void)
743744
}
744745
}
745746

747+
if (lcache_event_init() < 0) {
748+
LM_ERR("failed to init cachedb_local events\n");
749+
return -1;
750+
}
751+
746752
/* register timer to delete the expired entries */
747753
register_timer("localcache-expire",localcache_clean, 0,
748754
cache_clean_period, TIMER_FLAG_DELAY_ON_DELAY);
@@ -815,19 +821,29 @@ static void destroy(void)
815821
}
816822
}
817823

824+
/* temporary list node for collecting expired entries outside the lock */
825+
struct expired_ev {
826+
str key;
827+
str value;
828+
struct expired_ev *next;
829+
};
830+
818831
void localcache_clean(unsigned int ticks,void *param)
819832
{
820833
int i;
821834
lcache_entry_t* me1, *me2;
822835
lcache_col_t* it;
823836
lcache_t* cache_htable;
837+
struct expired_ev *ev_list, *ev, *ev_next;
824838

825839
for ( it=lcache_collection; it; it=it->next ) {
826840
LM_DBG("start\n");
827841
cache_htable = it->col_htable->htable;
828842

829843
for(i = 0; i< it->col_htable->size; i++)
830844
{
845+
ev_list = NULL;
846+
831847
lock_get(&cache_htable[i].lock);
832848
me1 = cache_htable[i].entries;
833849
me2 = NULL;
@@ -839,6 +855,25 @@ void localcache_clean(unsigned int ticks,void *param)
839855
LM_DBG("deleted entry attr= [%.*s]\n",
840856
me1->attr.len, me1->attr.s);
841857

858+
/* save key/value for event after lock release */
859+
if (ei_lcache_expired_id != EVI_ERROR) {
860+
ev = pkg_malloc(sizeof(*ev) +
861+
me1->attr.len + me1->value.len);
862+
if (ev) {
863+
ev->key.s = (char *)(ev + 1);
864+
ev->key.len = me1->attr.len;
865+
memcpy(ev->key.s, me1->attr.s,
866+
me1->attr.len);
867+
ev->value.s = ev->key.s +
868+
me1->attr.len;
869+
ev->value.len = me1->value.len;
870+
memcpy(ev->value.s, me1->value.s,
871+
me1->value.len);
872+
ev->next = ev_list;
873+
ev_list = ev;
874+
}
875+
}
876+
842877
if(me2)
843878
{
844879
me2->next = me1->next;
@@ -863,6 +898,15 @@ void localcache_clean(unsigned int ticks,void *param)
863898
}
864899

865900
lock_release(&cache_htable[i].lock);
901+
902+
/* raise events outside the lock to avoid deadlocks
903+
* if the event_route handler accesses the cache */
904+
for (ev = ev_list; ev; ev = ev_next) {
905+
ev_next = ev->next;
906+
lcache_raise_expired_event(&ev->key,
907+
&ev->value, &it->col_name);
908+
pkg_free(ev);
909+
}
866910
}
867911
}
868912
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright (C) 2026 OpenSIPS Solutions
3+
*
4+
* This file is part of opensips, a free SIP server.
5+
*
6+
* opensips is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* opensips is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
21+
#include "cachedb_local_evi.h"
22+
#include "../../dprint.h"
23+
#include "../../mem/mem.h"
24+
25+
event_id_t ei_lcache_expired_id = EVI_ERROR;
26+
27+
static evi_params_p lcache_expired_params;
28+
static evi_param_p lcache_ev_key;
29+
static evi_param_p lcache_ev_value;
30+
static evi_param_p lcache_ev_collection;
31+
32+
int lcache_event_init(void)
33+
{
34+
ei_lcache_expired_id = evi_publish_event(
35+
str_init(LCACHE_EV_EXPIRED));
36+
if (ei_lcache_expired_id == EVI_ERROR) {
37+
LM_ERR("cannot register %s event\n", LCACHE_EV_EXPIRED);
38+
return -1;
39+
}
40+
41+
lcache_expired_params = pkg_malloc(sizeof(evi_params_t));
42+
if (!lcache_expired_params) {
43+
LM_ERR("no more pkg memory\n");
44+
return -1;
45+
}
46+
memset(lcache_expired_params, 0, sizeof(evi_params_t));
47+
48+
lcache_ev_key = evi_param_create(lcache_expired_params,
49+
_str(LCACHE_EV_PARAM_KEY));
50+
if (!lcache_ev_key)
51+
goto error;
52+
53+
lcache_ev_value = evi_param_create(lcache_expired_params,
54+
_str(LCACHE_EV_PARAM_VALUE));
55+
if (!lcache_ev_value)
56+
goto error;
57+
58+
lcache_ev_collection = evi_param_create(lcache_expired_params,
59+
_str(LCACHE_EV_PARAM_COLLECTION));
60+
if (!lcache_ev_collection)
61+
goto error;
62+
63+
return 0;
64+
65+
error:
66+
LM_ERR("cannot create event parameter\n");
67+
return -1;
68+
}
69+
70+
void lcache_raise_expired_event(const str *key, const str *value,
71+
const str *collection)
72+
{
73+
if (ei_lcache_expired_id == EVI_ERROR || !evi_probe_event(ei_lcache_expired_id))
74+
return;
75+
76+
if (evi_param_set_str(lcache_ev_key, key) < 0) {
77+
LM_ERR("cannot set key parameter\n");
78+
return;
79+
}
80+
81+
if (evi_param_set_str(lcache_ev_value, value) < 0) {
82+
LM_ERR("cannot set value parameter\n");
83+
return;
84+
}
85+
86+
if (evi_param_set_str(lcache_ev_collection, collection) < 0) {
87+
LM_ERR("cannot set collection parameter\n");
88+
return;
89+
}
90+
91+
if (evi_raise_event(ei_lcache_expired_id, lcache_expired_params) < 0)
92+
LM_ERR("cannot raise %s event\n", LCACHE_EV_EXPIRED);
93+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (C) 2026 OpenSIPS Solutions
3+
*
4+
* This file is part of opensips, a free SIP server.
5+
*
6+
* opensips is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 2 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* opensips is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, write to the Free Software
18+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
21+
#ifndef _CACHEDB_LOCAL_EVI_H_
22+
#define _CACHEDB_LOCAL_EVI_H_
23+
24+
#include "../../evi/evi_modules.h"
25+
#include "../../evi/evi_params.h"
26+
27+
#define LCACHE_EV_EXPIRED "E_CACHEDB_LOCAL_EXPIRED"
28+
29+
#define LCACHE_EV_PARAM_KEY "key"
30+
#define LCACHE_EV_PARAM_VALUE "value"
31+
#define LCACHE_EV_PARAM_COLLECTION "collection"
32+
33+
extern event_id_t ei_lcache_expired_id;
34+
35+
int lcache_event_init(void);
36+
void lcache_raise_expired_event(const str *key, const str *value,
37+
const str *collection);
38+
39+
#endif /* _CACHEDB_LOCAL_EVI_H_ */

0 commit comments

Comments
 (0)