Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
kjdev committed Mar 26, 2024
1 parent 2b11b8b commit c2a540e
Show file tree
Hide file tree
Showing 7 changed files with 566 additions and 5 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,17 @@ The database is stored in shared memory or Redis as specified
by the zone parameter.

```
Syntax: keyval_zone zone=name:size;
Syntax: keyval_zone zone=name:size [timeout=time];
Default: -
Context: http
```

Sets the `name` and `size` of the shared memory zone that
keeps the key-value database.

The optional `timeout` parameter sets the time to live
which key-value pairs are removed (default value is `0` seconds).

```
Syntax: keyval_zone_redis zone=name [hostname=name] [port=number] [database=number] [connect_timeout=time] [ttl=time];
Default: -
Expand Down Expand Up @@ -145,14 +148,16 @@ The database is stored in shared memory or Redis as specified
by the zone parameter.

```
Syntax: keyval_zone zone=name:size;
Syntax: keyval_zone zone=name:size [timeout=time];
Default: -
Context: http
```

Sets the `name` and `size` of the shared memory zone that
keeps the key-value database.

The optional `timeout` parameter sets the time to live which key-value pairs are removed (default value is 0 seconds).

```
Syntax: keyval_zone_redis zone=name [hostname=name] [port=number] [database=number] [connect_timeout=time] [ttl=time];
Default: -
Expand Down Expand Up @@ -187,4 +192,4 @@ Example
TODO
----

- [ ] Support for `[state=file]`, `[timeout=time]` in `keyval_zone` directive
- [ ] Support for `[state=file]` in `keyval_zone` directive
2 changes: 1 addition & 1 deletion src/ngx_http_keyval_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ static ngx_command_t ngx_http_keyval_commands[] = {
0,
NULL },
{ ngx_string("keyval_zone"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
NGX_HTTP_MAIN_CONF|NGX_CONF_1MORE,
ngx_http_keyval_conf_set_zone,
0,
0,
Expand Down
64 changes: 64 additions & 0 deletions src/ngx_keyval.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <ngx_event.h>
#include "ngx_keyval.h"

static void
Expand Down Expand Up @@ -267,6 +268,26 @@ ngx_keyval_conf_set_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf,
shm_zone->init = ngx_keyval_init_zone;
shm_zone->data = ctx;

if (cf->args->nelts >= 2) {
if (ngx_strncmp(value[2].data, "timeout=", 8) == 0 && value[2].len > 4) {
ngx_str_t s;

s.len = value[2].len - 8;
s.data = value[2].data + 8;

ctx->ttl = ngx_parse_time(&s, 1);
if (ctx->ttl == (time_t) NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"\"%V\" invalid timeout \"%V\"",
&cmd->name, &value[2]);
ctx->ttl = 0;
return NGX_CONF_ERROR;
}
}
} else {
ctx->ttl = 0;
}

return NGX_CONF_OK;
}

Expand Down Expand Up @@ -550,6 +571,19 @@ ngx_keyval_shm_get_data(ngx_keyval_shm_ctx_t *ctx, ngx_shm_zone_t *shm,
return NGX_OK;
}

static void
ngx_keyval_delete_timeout_node_shm(ngx_event_t *node_status)
{
ngx_keyval_node_timeout_t *arg
= (ngx_keyval_node_timeout_t *) node_status->data;

if (arg->ctx->shpool != NULL && arg->node != NULL) {
ngx_rbtree_delete(&arg->ctx->sh->rbtree, arg->node);
ngx_slab_free(arg->ctx->shpool, arg->node);
ngx_slab_free(arg->ctx->shpool, arg);
}
}

ngx_int_t
ngx_keyval_shm_set_data(ngx_keyval_shm_ctx_t *ctx, ngx_shm_zone_t *shm,
ngx_str_t *key, ngx_str_t *val, ngx_log_t *log)
Expand Down Expand Up @@ -596,6 +630,36 @@ ngx_keyval_shm_set_data(ngx_keyval_shm_ctx_t *ctx, ngx_shm_zone_t *shm,
ngx_rbtree_insert(&ctx->sh->rbtree, node);

rc = NGX_OK;

if (ctx->ttl) {
ngx_event_t *timeout_node_event
= ngx_slab_alloc_locked(ctx->shpool, sizeof(ngx_event_t));

if (timeout_node_event == NULL) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"keyval: failed to allocate timeout event");
rc = NGX_ERROR;
} else {
ngx_keyval_node_timeout_t *timeout_node
= ngx_slab_alloc_locked(ctx->shpool,
sizeof(ngx_keyval_node_timeout_t));

if (timeout_node == NULL) {
ngx_log_error(NGX_LOG_ERR, log, 0,
"keyval: failed to allocate timeout node");
rc = NGX_ERROR;
ngx_slab_free_locked(ctx->shpool, timeout_node_event);
} else {
timeout_node->node = node;
timeout_node->ctx = ctx;

timeout_node_event->data = (void *) timeout_node;
timeout_node_event->handler = ngx_keyval_delete_timeout_node_shm;
timeout_node_event->log = shm->shm.log;
ngx_add_timer(timeout_node_event, ctx->ttl * 1000);
}
}
}
}

ngx_shmtx_unlock(&ctx->shpool->mutex);
Expand Down
6 changes: 6 additions & 0 deletions src/ngx_keyval.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,14 @@ typedef struct {
typedef struct {
ngx_keyval_sh_t *sh;
ngx_slab_pool_t *shpool;
time_t ttl;
} ngx_keyval_shm_ctx_t;

typedef struct {
ngx_rbtree_node_t *node;
ngx_keyval_shm_ctx_t *ctx;
} ngx_keyval_node_timeout_t;

typedef struct {
u_char *hostname;
ngx_int_t port;
Expand Down
2 changes: 1 addition & 1 deletion src/ngx_stream_keyval_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ static ngx_command_t ngx_stream_keyval_commands[] = {
0,
NULL },
{ ngx_string("keyval_zone"),
NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,
NGX_STREAM_MAIN_CONF|NGX_CONF_1MORE,
ngx_stream_keyval_conf_set_zone,
0,
0,
Expand Down
199 changes: 199 additions & 0 deletions t/keyval.t
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,198 @@ location = /set2 {
"/get(key): 1:test1 2:test2\n"
]
=== timeout (1s)
--init
system "sleep 1"
--- http_config
keyval_zone zone=timeout:5m timeout=1s;
keyval $cookie_data_key $keyval_data zone=timeout;
--- config
location = /get {
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
location = /set {
set $keyval_data "test1s";
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
--- request eval
[
"GET /get",
"GET /set",
"GET /get"
]
--- more_headers eval
[
"Cookie: data_key=key",
"Cookie: data_key=key",
"Cookie: data_key=key"
]
--- response_body eval
[
"/get(key): \n",
"/set(key): test1s\n",
"/get(key): test1s\n"
]
=== timeout (5s)
--- wait: 5
--- http_config
keyval_zone zone=timeout:5m timeout=5s;
keyval $cookie_data_key $keyval_data zone=timeout;
--- config
location = /get {
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
location = /set {
set $keyval_data "test5s";
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
--- request eval
[
"GET /get",
"GET /set",
"GET /get"
]
--- more_headers eval
[
"Cookie: data_key=key",
"Cookie: data_key=key",
"Cookie: data_key=key"
]
--- response_body eval
[
"/get(key): \n",
"/set(key): test5s\n",
"/get(key): \n"
]
=== timeout (6s but with request within 5s)
--- wait: 5
--- http_config
keyval_zone zone=timeout:5m timeout=6s;
keyval $cookie_data_key $keyval_data zone=timeout;
--- config
location = /get {
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
location = /set {
set $keyval_data "test5s";
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
--- request eval
[
"GET /get",
"GET /set",
"GET /get"
]
--- more_headers eval
[
"Cookie: data_key=key",
"Cookie: data_key=key",
"Cookie: data_key=key"
]
--- response_body eval
[
"/get(key): \n",
"/set(key): test5s\n",
"/get(key): test5s\n"
]
=== timeout (5s but with request within 5.1s)
--- wait: 5.1
--- http_config
keyval_zone zone=timeout:5m timeout=5s;
keyval $cookie_data_key $keyval_data zone=timeout;
--- config
location = /get {
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
location = /set {
set $keyval_data "test5s";
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
--- request eval
[
"GET /get",
"GET /set",
"GET /get"
]
--- more_headers eval
[
"Cookie: data_key=key",
"Cookie: data_key=key",
"Cookie: data_key=key"
]
--- response_body eval
[
"/get(key): \n",
"/set(key): test5s\n",
"/get(key): \n"
]
=== timeout (1m)
--- http_config
keyval_zone zone=timeout:5m timeout=1m;
keyval $cookie_data_key $keyval_data zone=timeout;
--- config
location = /get {
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
location = /set {
set $keyval_data "test1m";
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
--- request eval
[
"GET /get",
"GET /set",
"GET /get"
]
--- more_headers eval
[
"Cookie: data_key=key",
"Cookie: data_key=key",
"Cookie: data_key=key"
]
--- response_body eval
[
"/get(key): \n",
"/set(key): test1m\n",
"/get(key): test1m\n"
]
=== timeout (1h)
--- http_config
keyval_zone zone=timeout:5m timeout=1h;
keyval $cookie_data_key $keyval_data zone=timeout;
--- config
location = /get {
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
location = /set {
set $keyval_data "test1h";
return 200 "$request_uri($cookie_data_key): $keyval_data\n";
}
--- request eval
[
"GET /get",
"GET /set",
"GET /get"
]
--- more_headers eval
[
"Cookie: data_key=key",
"Cookie: data_key=key",
"Cookie: data_key=key"
]
--- response_body eval
[
"/get(key): \n",
"/set(key): test1h\n",
"/get(key): test1h\n"
]
=== conf not found keyval_zone
--- http_config
keyval $cookie_data_key $keyval_data zone=test;
Expand All @@ -252,3 +444,10 @@ keyval_zone zone=test:1M;
keyval $cookie_data_key $keyval_data zone=test1;
--- config
--- must_die
=== conf invalid ttl
--- http_config
keyval_zone zone=test:1M timeout=e5s;
keyval $cookie_data_key $keyval_data zone=test;
--- config
--- must_die
Loading

0 comments on commit c2a540e

Please sign in to comment.