diff --git a/lib/resty/wasmx.lua b/lib/resty/wasmx.lua index 3fddad748..fe2b5aa7f 100644 --- a/lib/resty/wasmx.lua +++ b/lib/resty/wasmx.lua @@ -5,8 +5,10 @@ local base = require "resty.core.base" local C = ffi.C +local assert = assert local get_string_buf = base.get_string_buf local get_size_ptr = base.get_size_ptr +local DEBUG_MODE = ngx.config.debug ffi.cdef [[ @@ -17,6 +19,9 @@ ffi.cdef [[ typedef unsigned char u_char; typedef intptr_t ngx_int_t; + typedef uintptr_t ngx_uint_t; + typedef ngx_uint_t ngx_msec_t; + typedef struct ngx_log_s ngx_log_t; typedef struct ngx_wavm_t ngx_wasm_vm_t; typedef struct ngx_wasm_ops_plan_t ngx_wasm_plan_t; @@ -35,6 +40,9 @@ local _M = { FFI_ABORT = base.FFI_ABORT -- nil in OpenResty 1.21.4.1 and base.FFI_ABORT or -6, + FFI_BUSY = base.FFI_BUSY -- nil in OpenResty 1.25.3.2 + and base.FFI_BUSY + or -3, FFI_ERROR = base.FFI_ERROR, FFI_DONE = base.FFI_DONE, FFI_OK = base.FFI_OK, @@ -58,4 +66,11 @@ function _M.get_err_ptr() end +function _M.assert_debug(...) + if DEBUG_MODE then + return assert(...) + end +end + + return _M diff --git a/lib/resty/wasmx/shm.lua b/lib/resty/wasmx/shm.lua new file mode 100644 index 000000000..093d6056d --- /dev/null +++ b/lib/resty/wasmx/shm.lua @@ -0,0 +1,562 @@ +-- vim:set ts=4 sw=4 sts=4 et: + +local ffi = require "ffi" +local wasmx = require "resty.wasmx" + + +local C = ffi.C +local error = error +local type = type +local tonumber = tonumber +local min = math.min +local new_tab = table.new +local insert = table.insert +local str_fmt = string.format +local ffi_cast = ffi.cast +local ffi_fill = ffi.fill +local ffi_new = ffi.new +local ffi_str = ffi.string +local ngx_sleep = ngx.sleep +local ngx_log = ngx.log +local DEBUG = ngx.DEBUG +local FFI_OK = wasmx.FFI_OK +local FFI_ERROR = wasmx.FFI_ERROR +local FFI_DONE = wasmx.FFI_DONE +local FFI_BUSY = wasmx.FFI_BUSY +local FFI_ABORT = wasmx.FFI_ABORT +local FFI_DECLINED = wasmx.FFI_DECLINED +local assert_debug = wasmx.assert_debug + + +ffi.cdef [[ + typedef struct ngx_slab_pool_s ngx_slab_pool_t; + + typedef enum { + NGX_WA_SHM_TYPE_KV, + NGX_WA_SHM_TYPE_QUEUE, + NGX_WA_SHM_TYPE_METRICS, + } ngx_wa_shm_type_e; + + typedef enum { + NGX_WA_SHM_EVICTION_LRU, + NGX_WA_SHM_EVICTION_SLRU, + NGX_WA_SHM_EVICTION_NONE, + } ngx_wa_shm_eviction_e; + + typedef struct { + ngx_wa_shm_type_e type; + ngx_wa_shm_eviction_e eviction; + ngx_str_t name; + ngx_log_t *log; + ngx_slab_pool_t *shpool; + void *data; + } ngx_wa_shm_t; + + typedef enum { + NGX_WA_METRIC_COUNTER, + NGX_WA_METRIC_GAUGE, + NGX_WA_METRIC_HISTOGRAM, + } ngx_wa_metric_type_e; + + typedef struct { + ngx_uint_t value; + ngx_msec_t last_update; + } ngx_wa_metrics_gauge_t; + + typedef struct { + uint32_t upper_bound; + uint32_t count; + } ngx_wa_metrics_bin_t; + + typedef struct { + uint8_t n_bins; + ngx_wa_metrics_bin_t bins[]; + } ngx_wa_metrics_histogram_t; + + typedef union { + ngx_uint_t counter; + ngx_wa_metrics_gauge_t gauge; + ngx_wa_metrics_histogram_t *histogram; + } ngx_wa_metric_val_t; + + typedef struct { + ngx_wa_metric_type_e metric_type; + ngx_wa_metric_val_t slots[]; + } ngx_wa_metric_t; + + + typedef void (*ngx_wa_ffi_shm_setup_zones_handler)(ngx_wa_shm_t *shm); + + + ngx_int_t ngx_wa_ffi_shm_setup_zones(ngx_wa_ffi_shm_setup_zones_handler handler); + ngx_int_t ngx_wa_ffi_shm_iterate_keys(ngx_wa_shm_t *shm, + ngx_uint_t page_size, + ngx_uint_t *clast_index, + ngx_uint_t *cur_idx, + ngx_str_t **keys); + + ngx_uint_t ngx_wa_ffi_shm_kv_nelts(ngx_wa_shm_t *shm); + ngx_int_t ngx_wa_ffi_shm_kv_get(ngx_wa_shm_t *shm, + ngx_str_t *k, + ngx_str_t **v, + uint32_t *cas); + ngx_int_t ngx_wa_ffi_shm_kv_set(ngx_wa_shm_t *shm, + ngx_str_t *k, + ngx_str_t *v, + uint32_t cas, + unsigned *written); + + ngx_int_t ngx_wa_ffi_shm_metric_define(ngx_str_t *name, + ngx_wa_metric_type_e type, + uint32_t *metric_id); + ngx_int_t ngx_wa_ffi_shm_metric_increment(uint32_t metric_id, + ngx_uint_t value); + ngx_int_t ngx_wa_ffi_shm_metric_record(uint32_t metric_id, + ngx_uint_t value); + ngx_int_t ngx_wa_ffi_shm_metric_get(uint32_t metric_id, + ngx_str_t *name, + u_char *mbuf, size_t mbs, + u_char *hbuf, size_t hbs); + + void ngx_wa_ffi_shm_lock(ngx_wa_shm_t *shm); + void ngx_wa_ffi_shm_unlock(ngx_wa_shm_t *shm); + + ngx_int_t ngx_wa_ffi_shm_metrics_one_slot_size(); + ngx_int_t ngx_wa_ffi_shm_metrics_histogram_max_size(); +]] + + +local WASM_SHM_KEY = {} +local DEFAULT_KEYS_PAGE_SIZE = 500 + + +local _M = setmetatable({}, { + __index = function(_, k) + error("resty.wasmx.shm: no \"" .. k .. "\" shm configured", 2) + end, +}) + + +local _types = { + ffi_shm = { + SHM_TYPE_KV = 0, + SHM_TYPE_QUEUE = 1, + SHM_TYPE_METRICS = 2, + }, + ffi_metric = { + COUNTER = 0, + GAUGE = 1, + HISTOGRAM = 2, + } +} + +local _metric_type_set = { + [_types.ffi_metric.COUNTER] = true, + [_types.ffi_metric.GAUGE] = true, + [_types.ffi_metric.HISTOGRAM] = true, +} + +local _mbs = C.ngx_wa_ffi_shm_metrics_one_slot_size() +local _hbs = C.ngx_wa_ffi_shm_metrics_histogram_max_size() +local _mbuf = ffi_new("u_char[?]", _mbs) +local _hbuf = ffi_new("u_char[?]", _hbs) +local _kbuf = ffi_new("ngx_str_t *[?]", DEFAULT_KEYS_PAGE_SIZE) + + +local function shm_lock(zone) + C.ngx_wa_ffi_shm_lock(zone[WASM_SHM_KEY]) +end + + +local function shm_unlock(zone) + C.ngx_wa_ffi_shm_unlock(zone[WASM_SHM_KEY]) +end + + +local function key_iterator(ctx) + if ctx.i == tonumber(ctx.ccur_index[0]) then + ngx_sleep(0) -- TODO: support non-yielding phases + + ctx.i = 0 + ctx.ccur_index[0] = 0 + + local rc = C.ngx_wa_ffi_shm_iterate_keys(ctx.shm, ctx.page_size, + ctx.clast_index, ctx.ccur_index, ctx.ckeys) + + if rc == FFI_ABORT then + -- users must manage locking themselves (e.g. break condition in the for loop) + local zone_name = ffi_str(ctx.shm.name.data, ctx.shm.name.len) + local err = "attempt to call %s:iterate_keys() but the " .. + "shm zone is not locked; invoke :lock() before " .. + "and :unlock() after." + + error(str_fmt(err, zone_name), 2) + end + + if rc == FFI_DONE then + return + end + + assert_debug(rc == FFI_OK) + + ngx_log(DEBUG, "iterate_keys fetched a new page") + end + + local ckey = ctx.ckeys[ctx.i] + local key = ffi_str(ctx.ckeys[ctx.i].data, ctx.ckeys[ctx.i].len) + + ctx.i = ctx.i + 1 + + return key +end + + +local function shm_iterate_keys(zone, opts) + if opts ~= nil then + if type(opts) ~= "table" then + error("opts must be a table", 2) + end + + if opts.page_size ~= nil then + if type(opts.page_size) ~= "number" then + error("opts.page_size must be a number", 2) + end + + if opts.page_size < 1 then + error("opts.page_size must be > 0", 2) + end + end + end + + local ctx = { + zone = zone, + shm = zone[WASM_SHM_KEY], + page_size = opts and opts.page_size + and opts.page_size + or DEFAULT_KEYS_PAGE_SIZE, + i = 0, + clast_index = ffi_new("ngx_uint_t[1]"), + ccur_index = ffi_new("ngx_uint_t[1]"), + ckeys = page_size and ffi_new("ngx_str_t *[?]", page_size) or _kbuf, + } + + return key_iterator, ctx +end + + +local function shm_get_keys(zone, max_count) + if max_count == nil then + max_count = DEFAULT_KEYS_PAGE_SIZE + + elseif type(max_count) ~= "number" then + error("max_count must be a number", 2) + + elseif max_count < 0 then + error("max_count must be >= 0", 2) + end + + shm_lock(zone) + + local shm = zone[WASM_SHM_KEY] + local nkeys = tonumber(C.ngx_wa_ffi_shm_kv_nelts(shm)) + if nkeys == 0 then + shm_unlock(zone) + return {} + end + + if max_count > 0 then + nkeys = min(nkeys, max_count) + end + + local ctotal = ffi_new("ngx_uint_t[1]") + local ckeys = ffi_new("ngx_str_t *[?]", nkeys) + + C.ngx_wa_ffi_shm_iterate_keys(shm, nkeys, nil, ctotal, ckeys) + + shm_unlock(zone) + + local total = tonumber(ctotal[0]) + local keys = new_tab(0, total) + assert(total == nkeys) + + for i = 1, total do + keys[i] = ffi_str(ckeys[i - 1].data, ckeys[i - 1].len) + end + + return keys +end + + +local function shm_kv_get(zone, key) + if type(key) ~= "string" then + error("key must be a string", 2) + end + + local shm = zone[WASM_SHM_KEY] + local cname = ffi_new("ngx_str_t", { data = key, len = #key }) + local cvalue = ffi_new("ngx_str_t *[1]") + local ccas = ffi_new("uint32_t[1]") + + local rc = C.ngx_wa_ffi_shm_kv_get(shm, cname, cvalue, ccas) + if rc == FFI_DECLINED then + return nil + end + + assert_debug(rc == FFI_OK) + + return ffi_str(cvalue[0].data, cvalue[0].len), tonumber(ccas[0]) +end + + +local function shm_kv_set(zone, key, value, cas) + if type(key) ~= "string" then + error("key must be a string", 2) + end + + if type(value) ~= "string" then + error("value must be a string", 2) + end + + if cas == nil then + cas = 0 + + elseif type(cas) ~= "number" then + error("cas must be a number", 2) + end + + local shm = zone[WASM_SHM_KEY] + local cname = ffi_new("ngx_str_t", { data = key, len = #key }) + local cvalue = ffi_new("ngx_str_t", { data = value, len = #value }) + local written = ffi_new("unsigned[1]") + + local rc = C.ngx_wa_ffi_shm_kv_set(shm, cname, cvalue, cas, written) + if rc == FFI_ERROR then + return nil, "no memory" + end + + assert_debug(rc == FFI_OK) + + return tonumber(written[0]) +end + + +local function metrics_define(zone, name, metric_type) + if type(name) ~= "string" or name == "" then + error("name must be a non-empty string", 2) + end + + if not _metric_type_set[metric_type] then + local err = "metric_type must be one of" .. + " resty.wasmx.shm.metrics.COUNTER," .. + " resty.wasmx.shm.metrics.GAUGE, or" .. + " resty.wasmx.shm.metrics.HISTOGRAM" + + error(err, 2) + end + + name = "lua." .. name + + local cname = ffi_new("ngx_str_t", { data = name, len = #name }) + local m_id = ffi_new("uint32_t [1]") + + local rc = C.ngx_wa_ffi_shm_metric_define(cname, metric_type, m_id) + if rc == FFI_ERROR then + return nil, "no memory" + end + + if rc == FFI_BUSY then + return nil, "name too long" + end + + -- FFI_ABORT: unreachable + assert_debug(rc == FFI_OK) + + return tonumber(m_id[0]) +end + + +local function metrics_increment(zone, metric_id, value) + if type(metric_id) ~= "number" then + error("metric_id must be a number", 2) + end + + if value ~= nil and (type(value) ~= "number" or value < 1) then + error("value must be > 0", 2) + end + + value = value and value or 1 + + local rc = C.ngx_wa_ffi_shm_metric_increment(metric_id, value) + if rc == FFI_DECLINED then + return nil, "metric not found" + end + + assert_debug(rc == FFI_OK) + + return true +end + + +local function metrics_record(zone, metric_id, value) + if type(metric_id) ~= "number" then + error("metric_id must be a number", 2) + end + + if type(value) ~= "number" then + error("value must be a number", 2) + end + + local rc = C.ngx_wa_ffi_shm_metric_record(metric_id, value) + if rc == FFI_DECLINED then + return nil, "metric not found" + end + + assert_debug(rc == FFI_OK) + + return true +end + + +local function parse_cmetric(cmetric) + if cmetric.metric_type == _types.ffi_metric.COUNTER then + return { + type = "counter", + value = tonumber(cmetric.slots[0].counter), + } + end + + if cmetric.metric_type == _types.ffi_metric.GAUGE then + return { + type = "gauge", + value = tonumber(cmetric.slots[0].gauge.value), + } + end + + if cmetric.metric_type == _types.ffi_metric.HISTOGRAM then + local hbuf = cmetric.slots[0].histogram + local ch = ffi_cast("ngx_wa_metrics_histogram_t *", hbuf) + local h = { type = "histogram", value = {} } + + for i = 0, ch.n_bins do + local cb = ch.bins[i] + if cb.upper_bound == 0 then + break + end + + insert(h.value, { + ub = cb.upper_bound, + count = cb.count, + }) + end + + return h + end + + assert(false, "unreachable") +end + + +local function metrics_get_by_id(zone, metric_id) + if type(metric_id) ~= "number" then + error("metric_id must be a number", 2) + end + + ffi_fill(_mbuf, _mbs) + ffi_fill(_hbuf, _hbs) + + local rc = C.ngx_wa_ffi_shm_metric_get(metric_id, nil, + _mbuf, _mbs, + _hbuf, _hbs) + if rc == FFI_DECLINED then + return nil + end + + assert_debug(rc == FFI_OK) + + return parse_cmetric(ffi_cast("ngx_wa_metric_t *", _mbuf)) +end + + +--- +-- ngx_wasm_module internally prefixes metric names according to +-- where they have been defined, e.g. "pw.filter.*", "lua.*", or +-- "wa.*". +-- +-- metrics_get_by_name assumes it is retrieving a Lua-defined metric +-- and will by default prefix the given name with `lua.` +-- +-- This behavior can be disabled by passing `opts.prefix` as false. +local function metrics_get_by_name(zone, name, opts) + if type(name) ~= "string" or name == "" then + error("name must be a non-empty string", 2) + end + + if opts ~= nil then + if type(opts) ~= "table" then + error("opts must be a table", 2) + end + + if opts.prefix ~= nil and type(opts.prefix) ~= "boolean" then + error("opts.prefix must be a boolean", 2) + end + end + + ffi_fill(_mbuf, _mbs) + ffi_fill(_hbuf, _hbs) + + name = (opts and opts.prefix == false) and name or "lua." .. name + + local cname = ffi_new("ngx_str_t", { data = name, len = #name }) + + local rc = C.ngx_wa_ffi_shm_metric_get(0, cname, _mbuf, _mbs, _hbuf, _hbs) + if rc == FFI_DECLINED then + return nil + end + + assert_debug(rc == FFI_OK) + + return parse_cmetric(ffi_cast("ngx_wa_metric_t *", _mbuf)) +end + + +local _setup_zones_handler = ffi_cast("ngx_wa_ffi_shm_setup_zones_handler", +function(shm) + local zone_name = ffi_str(shm.name.data, shm.name.len) + _M[zone_name] = { + [WASM_SHM_KEY] = shm, + lock = shm_lock, + unlock = shm_unlock, + } + + if shm.type == _types.ffi_shm.SHM_TYPE_KV then + _M[zone_name].iterate_keys = shm_iterate_keys + _M[zone_name].get_keys = shm_get_keys + _M[zone_name].get = shm_kv_get + _M[zone_name].set = shm_kv_set + + elseif shm.type == _types.ffi_shm.SHM_TYPE_QUEUE then + -- NYI + + elseif shm.type == _types.ffi_shm.SHM_TYPE_METRICS then + _M[zone_name].get_keys = shm_get_keys + _M[zone_name].iterate_keys = shm_iterate_keys + + _M[zone_name].define = metrics_define + _M[zone_name].increment = metrics_increment + _M[zone_name].record = metrics_record + _M[zone_name].get = metrics_get_by_id + _M[zone_name].get_by_name = metrics_get_by_name + + _M[zone_name].COUNTER = _types.ffi_metric.COUNTER + _M[zone_name].GAUGE = _types.ffi_metric.GAUGE + _M[zone_name].HISTOGRAM = _types.ffi_metric.HISTOGRAM + end +end) + + +if C.ngx_wa_ffi_shm_setup_zones(_setup_zones_handler) == FFI_ABORT then + ngx_log(DEBUG, "no shm zones found for resty.wasmx.shm interface") +end + + +return _M diff --git a/src/common/debug/ngx_wasm_debug_module.c b/src/common/debug/ngx_wasm_debug_module.c index a73e50bdb..c21921565 100644 --- a/src/common/debug/ngx_wasm_debug_module.c +++ b/src/common/debug/ngx_wasm_debug_module.c @@ -55,7 +55,7 @@ ngx_wasm_debug_init(ngx_cycle_t *cycle) ngx_wa_metrics_define(ngx_wasmx_metrics(cycle), &metric_name, NGX_WA_METRIC_COUNTER, - &mid) == NGX_ABORT + &mid) == NGX_BUSY ); /* invalid metric type */ diff --git a/src/common/lua/ngx_wasm_lua_ffi.c b/src/common/lua/ngx_wasm_lua_ffi.c index 1c24ae600..35a1657df 100644 --- a/src/common/lua/ngx_wasm_lua_ffi.c +++ b/src/common/lua/ngx_wasm_lua_ffi.c @@ -4,7 +4,10 @@ #include "ddebug.h" #include +#include +#ifdef NGX_WASM_HTTP #include +#endif ngx_wavm_t * @@ -257,3 +260,201 @@ ngx_http_wasm_ffi_set_host_properties_handlers(ngx_http_request_t *r, return ngx_proxy_wasm_properties_set_ffi_handlers(pwctx, getter, setter, r); } #endif + + +ngx_int_t +ngx_wa_ffi_shm_setup_zones(ngx_wa_ffi_shm_setup_zones_handler_pt setup_handler) +{ + ngx_uint_t i; + ngx_array_t *shms = ngx_wasmx_shms((ngx_cycle_t *) ngx_cycle); + ngx_wa_shm_t *shm; + ngx_wa_shm_mapping_t *mappings; + + if (shms == NULL) { + return NGX_ABORT; + } + + mappings = shms->elts; + + for (i = 0; i < shms->nelts; i++) { + shm = mappings[i].zone->data; + setup_handler(shm); + } + + return NGX_OK; +} + + +static void +retrieve_keys(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, + ngx_uint_t limit, ngx_uint_t start_idx, ngx_uint_t *cur_idx, + ngx_str_t **keys) +{ + ngx_wa_shm_kv_node_t *n = (ngx_wa_shm_kv_node_t *) node; + + if ((*cur_idx - start_idx) == limit) { + return; + } + + if (*cur_idx >= start_idx) { + /* append to result */ + keys[(*cur_idx - start_idx)] = &n->key.str; + } + + (*cur_idx)++; + + if (node->left != sentinel) { + retrieve_keys(node->left, sentinel, limit, start_idx, cur_idx, keys); + } + + if (node->right != sentinel) { + retrieve_keys(node->right, sentinel, limit, start_idx, cur_idx, keys); + } +} + + +ngx_int_t +ngx_wa_ffi_shm_iterate_keys(ngx_wa_shm_t *shm, ngx_uint_t page_size, + ngx_uint_t *clast_idx, ngx_uint_t *cur_idx, ngx_str_t **keys) +{ + ngx_uint_t last_idx = clast_idx ? *clast_idx : 0; + ngx_wa_shm_kv_t *kv; + + ngx_wa_assert(shm->type == NGX_WA_SHM_TYPE_KV + || shm->type == NGX_WA_SHM_TYPE_METRICS); + + if (!ngx_wa_shm_locked(shm)) { + return NGX_ABORT; + } + + kv = shm->data; + + if (last_idx >= kv->nelts) { + /* finished */ + return NGX_DONE; + } + + /* cannot be empty here (kv->nelts > 0) */ + ngx_wa_assert(kv->rbtree.root != kv->rbtree.sentinel); + + retrieve_keys(kv->rbtree.root, kv->rbtree.sentinel, page_size, last_idx, + cur_idx, keys); + + *cur_idx -= last_idx; + + if (clast_idx) { + *clast_idx += *cur_idx; + } + + return NGX_OK; +} + + +ngx_uint_t +ngx_wa_ffi_shm_kv_nelts(ngx_wa_shm_t *shm) +{ + ngx_wa_shm_kv_t *kv; + + ngx_wa_assert(shm->type == NGX_WA_SHM_TYPE_KV + || shm->type == NGX_WA_SHM_TYPE_METRICS); + + kv = shm->data; + + return kv->nelts; +} + + +ngx_int_t +ngx_wa_ffi_shm_kv_get(ngx_wa_shm_t *shm, ngx_str_t *k, ngx_str_t **v, + uint32_t *cas) +{ + ngx_int_t rc; + unsigned unlock = 0; + + ngx_wa_assert(shm->type == NGX_WA_SHM_TYPE_KV); + + if (!ngx_wa_shm_locked(shm)) { + /* e.g. already locked if in iterate_keys() */ + ngx_wa_shm_lock(shm); + unlock = 1; + } + + rc = ngx_wa_shm_kv_get_locked(shm, k, NULL, v, cas); + + if (unlock) { + ngx_wa_shm_unlock(shm); + } + + return rc; +} + + +ngx_int_t +ngx_wa_ffi_shm_kv_set(ngx_wa_shm_t *shm, ngx_str_t *k, ngx_str_t *v, + uint32_t cas, unsigned *written) +{ + ngx_int_t rc; + + ngx_wa_assert(shm->type == NGX_WA_SHM_TYPE_KV); + + ngx_wa_shm_lock(shm); + + rc = ngx_wa_shm_kv_set_locked(shm, k, v, cas, written); + + ngx_wa_shm_unlock(shm); + + return rc; +} + + +ngx_int_t +ngx_wa_ffi_shm_metric_define(ngx_str_t *name, ngx_wa_metric_type_e type, + uint32_t *metric_id) +{ + ngx_int_t rc; + ngx_wa_metrics_t *metrics = ngx_wasmx_metrics((ngx_cycle_t *) ngx_cycle); + + rc = ngx_wa_metrics_define(metrics, name, type, metric_id); + if (rc != NGX_OK) { + return rc; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_wa_ffi_shm_metric_increment(uint32_t metric_id, ngx_uint_t value) +{ + ngx_wa_metrics_t *metrics = ngx_wasmx_metrics((ngx_cycle_t *) ngx_cycle); + + return ngx_wa_metrics_increment(metrics, metric_id, value); +} + + +ngx_int_t +ngx_wa_ffi_shm_metric_record(uint32_t metric_id, ngx_uint_t value) +{ + ngx_wa_metrics_t *metrics = ngx_wasmx_metrics((ngx_cycle_t *) ngx_cycle); + + return ngx_wa_metrics_record(metrics, metric_id, value); +} + + +ngx_int_t +ngx_wa_ffi_shm_metric_get(uint32_t metric_id, ngx_str_t *name, u_char *m_buf, + size_t mbs, u_char *h_buf, size_t hbs) +{ + ngx_wa_metrics_t *metrics = ngx_wasmx_metrics((ngx_cycle_t *) ngx_cycle); + ngx_wa_metric_t *m; + + m = (ngx_wa_metric_t *) m_buf; + ngx_wa_metrics_histogram_set_buffer(m, h_buf, hbs); + + if (metric_id) { + return ngx_wa_metrics_get(metrics, metric_id, m); + } + + return ngx_wa_metrics_get(metrics, + ngx_crc32_long(name->data, name->len), m); +} diff --git a/src/common/lua/ngx_wasm_lua_ffi.h b/src/common/lua/ngx_wasm_lua_ffi.h index 0ed226f86..87dc42881 100644 --- a/src/common/lua/ngx_wasm_lua_ffi.h +++ b/src/common/lua/ngx_wasm_lua_ffi.h @@ -24,6 +24,9 @@ typedef struct { } ngx_wasm_ffi_filter_t; +typedef void (*ngx_wa_ffi_shm_setup_zones_handler_pt)(ngx_wa_shm_t *shm); + + ngx_int_t ngx_http_wasm_ffi_plan_new(ngx_wavm_t *vm, ngx_wasm_ffi_filter_t *filters, size_t n_filters, ngx_wasm_ops_plan_t **out, u_char *err, size_t *errlen); @@ -43,4 +46,51 @@ ngx_int_t ngx_http_wasm_ffi_set_host_properties_handlers(ngx_http_request_t *r, #endif +ngx_int_t ngx_wa_ffi_shm_setup_zones( + ngx_wa_ffi_shm_setup_zones_handler_pt handler); +ngx_int_t ngx_wa_ffi_shm_iterate_keys(ngx_wa_shm_t *shm, ngx_uint_t page_size, + ngx_uint_t *clast_idx, ngx_uint_t *cur_idx, ngx_str_t **keys); + +ngx_uint_t ngx_wa_ffi_shm_kv_nelts(ngx_wa_shm_t *shm); +ngx_int_t ngx_wa_ffi_shm_kv_get(ngx_wa_shm_t *shm, ngx_str_t *k, + ngx_str_t **v, uint32_t *cas); +ngx_int_t ngx_wa_ffi_shm_kv_set(ngx_wa_shm_t *shm, ngx_str_t *k, + ngx_str_t *v, uint32_t cas, unsigned *written); + +ngx_int_t ngx_wa_ffi_shm_metric_define(ngx_str_t *name, + ngx_wa_metric_type_e type, uint32_t *metric_id); +ngx_int_t ngx_wa_ffi_shm_metric_increment(uint32_t metric_id, ngx_uint_t value); +ngx_int_t ngx_wa_ffi_shm_metric_record(uint32_t metric_id, ngx_uint_t value); +ngx_int_t ngx_wa_ffi_shm_metric_get(uint32_t metric_id, ngx_str_t *name, + u_char *m_buf, size_t mbs, u_char *h_buf, size_t hbs); + + +void +ngx_wa_ffi_shm_lock(ngx_wa_shm_t *shm) +{ + ngx_wa_shm_lock(shm); +} + + +void +ngx_wa_ffi_shm_unlock(ngx_wa_shm_t *shm) +{ + ngx_wa_shm_unlock(shm); +} + + +ngx_int_t +ngx_wa_ffi_shm_metrics_one_slot_size() +{ + return NGX_WA_METRICS_ONE_SLOT_SIZE; +} + + +ngx_int_t +ngx_wa_ffi_shm_metrics_histogram_max_size() +{ + return NGX_WA_METRICS_HISTOGRAM_MAX_SIZE; +} + + #endif /* _NGX_WASM_LUA_FFI_H_INCLUDED_ */ diff --git a/src/common/metrics/ngx_wa_histogram.c b/src/common/metrics/ngx_wa_histogram.c index d52e46cef..4aee8ddfa 100644 --- a/src/common/metrics/ngx_wa_histogram.c +++ b/src/common/metrics/ngx_wa_histogram.c @@ -129,7 +129,7 @@ histogram_log(ngx_wa_metrics_t *metrics, ngx_wa_metric_t *m, uint32_t mid) u_char *p; ngx_wa_metrics_bin_t *b; ngx_wa_metrics_histogram_t *h; - u_char h_buf[NGX_WA_METRICS_MAX_HISTOGRAM_SIZE]; + u_char h_buf[NGX_WA_METRICS_HISTOGRAM_MAX_SIZE]; u_char s_buf[NGX_MAX_ERROR_STR]; ngx_memzero(h_buf, sizeof(h_buf)); diff --git a/src/common/metrics/ngx_wa_metrics.c b/src/common/metrics/ngx_wa_metrics.c index e0579e693..3bcdc0a4f 100644 --- a/src/common/metrics/ngx_wa_metrics.c +++ b/src/common/metrics/ngx_wa_metrics.c @@ -270,6 +270,7 @@ ngx_wa_metrics_shm_init(ngx_cycle_t *cycle) /** * NGX_OK: success + * NGX_BUSY: name too long * NGX_ABORT: bad usage * NGX_ERROR: no memory */ @@ -280,7 +281,8 @@ ngx_wa_metrics_define(ngx_wa_metrics_t *metrics, ngx_str_t *name, ssize_t size = sizeof(ngx_wa_metric_t) + sizeof(ngx_wa_metric_val_t) * metrics->workers; uint32_t cas, mid; - ngx_int_t rc, written; + unsigned written; + ngx_int_t rc; ngx_str_t *p, val; ngx_wa_metric_t *m; u_char buf[size]; @@ -293,7 +295,7 @@ ngx_wa_metrics_define(ngx_wa_metrics_t *metrics, ngx_str_t *name, } if (name->len > metrics->config.max_metric_name_length) { - return NGX_ABORT; + return NGX_BUSY; } mid = ngx_crc32_long(name->data, name->len); @@ -337,9 +339,9 @@ ngx_wa_metrics_define(ngx_wa_metrics_t *metrics, ngx_str_t *name, ngx_wa_shm_unlock(metrics->shm); if (rc == NGX_OK) { - ngx_wasm_log_error(NGX_LOG_INFO, metrics->shm->log, 0, - "defined %V \"%V\" with id %uD", - ngx_wa_metric_type_name(type), name, mid); + ngx_log_debug3(NGX_LOG_DEBUG_WASM, metrics->shm->log, 0, + "defined %V \"%V\" with id %uD", + ngx_wa_metric_type_name(type), name, mid); } ngx_wa_assert(rc == NGX_OK || rc == NGX_ERROR); diff --git a/src/common/metrics/ngx_wa_metrics.h b/src/common/metrics/ngx_wa_metrics.h index 00942b45c..49ad6de25 100644 --- a/src/common/metrics/ngx_wa_metrics.h +++ b/src/common/metrics/ngx_wa_metrics.h @@ -12,11 +12,12 @@ #define NGX_WA_METRICS_BINS_INIT 5 #define NGX_WA_METRICS_BINS_MAX 18 #define NGX_WA_METRICS_BINS_INCREMENT 4 -#define NGX_WA_METRICS_MAX_HISTOGRAM_SIZE sizeof(ngx_wa_metrics_histogram_t)\ - + sizeof(ngx_wa_metrics_bin_t) \ - * NGX_WA_METRICS_BINS_MAX -#define NGX_WA_METRICS_ONE_SLOT_METRIC_SIZE sizeof(ngx_wa_metric_t) \ +#define NGX_WA_METRICS_ONE_SLOT_SIZE sizeof(ngx_wa_metric_t) \ + sizeof(ngx_wa_metric_val_t) +#define NGX_WA_METRICS_HISTOGRAM_MAX_SIZE \ + sizeof(ngx_wa_metrics_histogram_t) \ + + sizeof(ngx_wa_metrics_bin_t) \ + * NGX_WA_METRICS_BINS_MAX typedef struct ngx_wa_metrics_s ngx_wa_metrics_t; diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_host.c b/src/common/proxy_wasm/ngx_proxy_wasm_host.c index cf0f6c3f7..48520bc87 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm_host.c +++ b/src/common/proxy_wasm/ngx_proxy_wasm_host.c @@ -1317,7 +1317,8 @@ ngx_proxy_wasm_hfuncs_set_shared_data(ngx_wavm_instance_t *instance, wasm_val_t args[], wasm_val_t rets[]) { uint32_t cas; - ngx_int_t rc, written; + unsigned written; + ngx_int_t rc; ngx_str_t key, value; ngx_wa_shm_kv_key_t resolved; ngx_proxy_wasm_exec_t *pwexec = ngx_proxy_wasm_instance2pwexec(instance); @@ -1601,7 +1602,7 @@ ngx_proxy_wasm_hfuncs_define_metric(ngx_wavm_instance_t *instance, ngx_memzero(trapmsg, NGX_MAX_ERROR_STR); - switch(pw_type) { + switch (pw_type) { case NGX_PROXY_WASM_METRIC_COUNTER: type = NGX_WA_METRIC_COUNTER; break; @@ -1648,8 +1649,10 @@ ngx_proxy_wasm_hfuncs_define_metric(ngx_wavm_instance_t *instance, rets, NGX_WAVM_ERROR); default: - ngx_wa_assert(rc == NGX_OK); - break; + if (rc != NGX_OK) { + return ngx_proxy_wasm_result_trap(pwexec, "unknown error", + rets, NGX_WAVM_ERROR); + } } return ngx_proxy_wasm_result_ok(rets); @@ -1668,8 +1671,8 @@ ngx_proxy_wasm_hfuncs_get_metric(ngx_wavm_instance_t *instance, ngx_wa_metrics_t *metrics = ngx_wasmx_metrics(cycle); ngx_wa_metric_t *m; ngx_proxy_wasm_exec_t *pwexec = ngx_proxy_wasm_instance2pwexec(instance); - u_char m_buf[NGX_WA_METRICS_ONE_SLOT_METRIC_SIZE]; - u_char h_buf[NGX_WA_METRICS_MAX_HISTOGRAM_SIZE]; + u_char m_buf[NGX_WA_METRICS_ONE_SLOT_SIZE]; + u_char h_buf[NGX_WA_METRICS_HISTOGRAM_MAX_SIZE]; u_char trapmsg[NGX_MAX_ERROR_STR]; ngx_memzero(m_buf, sizeof(m_buf)); diff --git a/src/common/shm/ngx_wa_shm.h b/src/common/shm/ngx_wa_shm.h index 671f79656..1e21fe862 100644 --- a/src/common/shm/ngx_wa_shm.h +++ b/src/common/shm/ngx_wa_shm.h @@ -59,4 +59,11 @@ ngx_wa_shm_unlock(ngx_wa_shm_t *shm) } +static ngx_inline unsigned +ngx_wa_shm_locked(ngx_wa_shm_t *shm) +{ + return (ngx_pid_t) *shm->shpool->mutex.lock == ngx_pid; +} + + #endif /* _NGX_WA_SHM_H_INCLUDED_ */ diff --git a/src/common/shm/ngx_wa_shm_kv.c b/src/common/shm/ngx_wa_shm_kv.c index a2a1d2704..690ee173f 100644 --- a/src/common/shm/ngx_wa_shm_kv.c +++ b/src/common/shm/ngx_wa_shm_kv.c @@ -266,7 +266,7 @@ node_queue_remove(ngx_wa_shm_t *shm, ngx_wa_shm_kv_node_t *n) ngx_int_t ngx_wa_shm_kv_set_locked(ngx_wa_shm_t *shm, ngx_str_t *key, - ngx_str_t *value, uint32_t cas, ngx_int_t *written) + ngx_str_t *value, uint32_t cas, unsigned *written) { size_t size; uint32_t key_hash = ngx_crc32_long(key->data, key->len); diff --git a/src/common/shm/ngx_wa_shm_kv.h b/src/common/shm/ngx_wa_shm_kv.h index 51a30bdb9..4f126dfad 100644 --- a/src/common/shm/ngx_wa_shm_kv.h +++ b/src/common/shm/ngx_wa_shm_kv.h @@ -38,7 +38,7 @@ ngx_int_t ngx_wa_shm_kv_init(ngx_wa_shm_t *shm); ngx_int_t ngx_wa_shm_kv_get_locked(ngx_wa_shm_t *shm, ngx_str_t *key, uint32_t *key_hash, ngx_str_t **value_out, uint32_t *cas); ngx_int_t ngx_wa_shm_kv_set_locked(ngx_wa_shm_t *shm, - ngx_str_t *key, ngx_str_t *value, uint32_t cas, ngx_int_t *written); + ngx_str_t *key, ngx_str_t *value, uint32_t cas, unsigned *written); ngx_int_t ngx_wa_shm_kv_resolve_key(ngx_str_t *key, ngx_wa_shm_kv_key_t *out); diff --git a/src/ngx_wasmx.c b/src/ngx_wasmx.c index 0260d4592..b4470c517 100644 --- a/src/ngx_wasmx.c +++ b/src/ngx_wasmx.c @@ -347,6 +347,7 @@ ngx_wasmx_metrics(ngx_cycle_t *cycle) ngx_wa_conf_t *wacf; if (cycle->conf_ctx == NULL) { + /* cycle->old_cycle */ return NULL; } diff --git a/t/04-openresty/ffi/shm/000-setup_zones.t b/t/04-openresty/ffi/shm/000-setup_zones.t new file mode 100644 index 000000000..360417d4d --- /dev/null +++ b/t/04-openresty/ffi/shm/000-setup_zones.t @@ -0,0 +1,140 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +add_block_preprocessor(sub { + my $block = shift; + if (!defined $block->main_config) { + $block->set_value("main_config", <<_EOC_ + wasm { + shm_kv kv 16k; + shm_queue q 16k; + } +_EOC_ + ); + } +}); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: shm - setup_zones() sanity +setup_zones() is silently called when resty.wasmx.shm module is loaded. +--- valgrind +--- main_config + wasm { + shm_kv kv1 16k; + shm_queue q1 16k; + } +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + assert(shm.kv1) + assert(shm.q1) + assert(shm.metrics) + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] + + + +=== TEST 2: shm - setup_zones() no zones +--- skip_no_debug +--- main_config +--- valgrind +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + ngx.say("ok") + } + } +--- response_body +ok +--- error_log +no shm zones found for resty.wasmx.shm interface +--- no_error_log +[error] + + + +=== TEST 3: shm - setup_zones() bad zone indexing +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + print(shm.bad) + } + } +--- error_code: 500 +--- response_body_like: 500 Internal Server Error +--- error_log eval +qr/runtime error: access_by_lua\(nginx\.conf:\d+\):4: resty\.wasmx\.shm: no "bad" shm configured/ +--- no_error_log +[crit] + + + +=== TEST 4: shm - calling functions on bad shm types +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local _, perr = pcall(function() + shm.q:get() + end) + ngx.say(perr) + + _, perr = pcall(function() + shm.q:set() + end) + ngx.say(perr) + + _, perr = pcall(function() + shm.q:iterate_keys() + end) + ngx.say(perr) + + _, perr = pcall(function() + shm.q:get_keys() + end) + ngx.say(perr) + + _, perr = pcall(function() + shm.metrics:set() + end) + ngx.say(perr) + + _, perr = pcall(function() + shm.kv:define() + end) + ngx.say(perr) + } + } +--- response_body_like +.*? attempt to call method 'get' \(a nil value\) +.*? attempt to call method 'set' \(a nil value\) +.*? attempt to call method 'iterate_keys' \(a nil value\) +.*? attempt to call method 'get_keys' \(a nil value\) +.*? attempt to call method 'set' \(a nil value\) +.*? attempt to call method 'define' \(a nil value\) +--- no_error_log +[crit] +[emerg] diff --git a/t/04-openresty/ffi/shm/001-kv_get.t b/t/04-openresty/ffi/shm/001-kv_get.t new file mode 100644 index 000000000..b9c79a118 --- /dev/null +++ b/t/04-openresty/ffi/shm/001-kv_get.t @@ -0,0 +1,114 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: shm_kv - get() sanity +--- valgrind +--- wasm_modules: hostcalls +--- shm_kv: kv 16k +--- config + location /t { + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv/k1 \ + value=v1'; + + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv/k2 \ + value=v2'; + + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local v, cas = shm.kv:get("kv/k1") + assert(cas == 1) + ngx.say(v) + + v, cas = shm.kv:get("kv/k2") + assert(cas == 1) + ngx.say(v) + } + } +--- response_body +v1 +v2 +--- no_error_log +[error] +[crit] + + + +=== TEST 2: shm_kv - get() metric not found +--- valgrind +--- shm_kv: kv 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local v, cas = shm.kv:get("none") + assert(cas == nil) + ngx.say(v) + } + } +--- response_body +nil +--- no_error_log +[error] +[crit] + + + +=== TEST 3: shm_kv - get() bad args +--- shm_kv: kv 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local _, perr = pcall(shm.kv.get, {}, false) + ngx.say(perr) + } + } +--- response_body +key must be a string +--- no_error_log +[crit] +[emerg] + + + +=== TEST 4: shm_kv - get() on locked shm +Do not wait on lock if shm is already locked. +--- wasm_modules: hostcalls +--- shm_kv: kv 16k +--- config + location /t { + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv/k1 \ + value=v1'; + + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.kv:lock() + + local v = shm.kv:get("kv/k1") + ngx.say(v) + + shm.kv:unlock() + } + } +--- response_body +v1 +--- no_error_log +[error] +[crit] diff --git a/t/04-openresty/ffi/shm/002-kv_set.t b/t/04-openresty/ffi/shm/002-kv_set.t new file mode 100644 index 000000000..68f821a90 --- /dev/null +++ b/t/04-openresty/ffi/shm/002-kv_set.t @@ -0,0 +1,129 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: shm_kv - set() sanity +--- valgrind +--- wasm_modules: hostcalls +--- shm_kv: kv 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local written, err = shm.kv:set("kv/k1", "v1") + assert(err == nil) + assert(written == 1) + + written, err = shm.kv:set("kv/k2", "v2") + assert(err == nil) + assert(written == 1) + + ngx.say("ok") + } + + proxy_wasm hostcalls 'test=/t/shm/log_shared_data \ + on=log \ + key=kv/k1'; + + proxy_wasm hostcalls 'test=/t/shm/log_shared_data \ + on=log \ + key=kv/k2'; + } +--- ignore_response_body +--- error_log +kv/k1: "v1" 1 +kv/k2: "v2" 1 +--- no_error_log +[error] + + + +=== TEST 2: shm_kv - set() no memory +--- shm_kv: kv 16k eviction=none +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + for i = 1, 5 do + local _, err = shm.kv:set(tostring(i), string.rep(".", 2^10)) + if err then + ngx.log(ngx.ERR, err) + break + end + end + + ngx.say("ok") + } + } +--- ignore_response_body +--- error_log eval +[ + qr/\[crit\] .*? "kv" shm store: no memory; cannot allocate pair with key size 1 and value size 1024/, + qr/\[error\] .*? access_by_lua\(.*?\):\d+: no memory/, +] +--- no_error_log +[emerg] + + + +=== TEST 3: shm_kv - set() bad args +--- shm_kv: kv 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local _, perr = pcall(shm.kv.set, {}, false) + ngx.say(perr) + + _, perr = pcall(shm.kv.set, {}, "k1", false) + ngx.say(perr) + + _, perr = pcall(shm.kv.set, {}, "k1", "v1", false) + ngx.say(perr) + } + } +--- response_body +key must be a string +value must be a string +cas must be a number +--- no_error_log +[crit] +[emerg] + + + +=== TEST 4: shm_kv - set() on locked shm +--- timeout: 1 +--- abort +--- shm_kv: kv 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.kv:lock() + + shm.kv:set("kv/k1", "v1") + + shm.kv:unlock() + + ngx.say("fail") + } + } +--- error_code: +--- no_error_log +[error] +[crit] +[emerg] diff --git a/t/04-openresty/ffi/shm/010-iterate_keys.t b/t/04-openresty/ffi/shm/010-iterate_keys.t new file mode 100644 index 000000000..07bfa2668 --- /dev/null +++ b/t/04-openresty/ffi/shm/010-iterate_keys.t @@ -0,0 +1,223 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +add_block_preprocessor(sub { + my $block = shift; + if (!defined $block->main_config) { + $block->set_value("main_config", <<_EOC_ + wasm { + shm_kv kv 16k; + shm_queue q 16k; + } +_EOC_ + ); + } +}); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: shm - iterate_keys() with page_size > nelts +--- skip_no_debug +--- valgrind +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.kv:set("k1", "value") + shm.kv:set("k2", "value") + shm.kv:set("k3", "value") + + shm.kv:lock() + + for k in shm.kv:iterate_keys() do + ngx.say(k) + end + + shm.kv:unlock() + } + } +--- response_body +k3 +k2 +k1 +--- grep_error_log: iterate_keys fetched a new page +--- grep_error_log_out +iterate_keys fetched a new page +--- no_error_log +[error] + + + +=== TEST 2: shm - iterate_keys() with page_size < nelts +--- skip_no_debug +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.kv:set("k1", "value") + shm.kv:set("k2", "value") + shm.kv:set("k3", "value") + + shm.kv:lock() + + for k in shm.kv:iterate_keys({ page_size = 2 }) do + ngx.say(k) + end + + shm.kv:unlock() + } + } +--- response_body +k3 +k2 +k1 +--- grep_error_log: iterate_keys fetched a new page +--- grep_error_log_out +iterate_keys fetched a new page +iterate_keys fetched a new page +--- no_error_log +[error] + + + +=== TEST 3: shm - iterate_keys() with page_size == nelts +--- skip_no_debug +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.kv:set("k1", "value") + shm.kv:set("k2", "value") + shm.kv:set("k3", "value") + + shm.kv:lock() + + for k in shm.kv:iterate_keys({ page_size = 3 }) do + ngx.say(k) + end + + shm.kv:unlock() + } + } +--- response_body +k3 +k2 +k1 +--- grep_error_log: iterate_keys fetched a new page +--- grep_error_log_out +iterate_keys fetched a new page +--- no_error_log +[error] + + + +=== TEST 4: shm - iterate_keys() on empty shm +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.kv:lock() + + for k in shm.kv:iterate_keys() do + ngx.say("fail") + end + + shm.kv:unlock() + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] + + + +=== TEST 5: shm - iterate_keys() on unlocked shm +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + for k in shm.kv:iterate_keys() do + + end + } + } +--- error_code: 500 +--- error_log eval +qr/\[error\] .*? attempt to call kv:iterate_keys\(\) but the shm zone is not locked; invoke :lock\(\) before and :unlock\(\) after./ +--- no_error_log +[crit] +[emerg] + + + +=== TEST 6: shm - iterate_keys() bad args +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local _, perr = pcall(shm.kv.iterate_keys, {}, true) + ngx.say(perr) + + _, perr = pcall(shm.kv.iterate_keys, {}, { page_size = true }) + ngx.say(perr) + + _, perr = pcall(shm.kv.iterate_keys, {}, { page_size = 0 }) + ngx.say(perr) + } + } +--- response_body +opts must be a table +opts.page_size must be a number +opts.page_size must be > 0 +--- no_error_log +[crit] +[emerg] + + + +=== TEST 7: shm - iterate_keys() can use get() while iterating +get() does not wait on lock if the shm is already locked. +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.kv:set("k1", "v1") + shm.kv:set("k2", "v2") + shm.kv:set("k3", "v3") + + shm.kv:lock() + + for k in shm.kv:iterate_keys() do + local v = shm.kv:get(k) + ngx.say(v) + end + + shm.kv:unlock() + } + } +--- response_body +v3 +v2 +v1 +--- no_error_log +[error] +[crit] diff --git a/t/04-openresty/ffi/shm/011-get_keys.t b/t/04-openresty/ffi/shm/011-get_keys.t new file mode 100644 index 000000000..5396d6003 --- /dev/null +++ b/t/04-openresty/ffi/shm/011-get_keys.t @@ -0,0 +1,114 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +add_block_preprocessor(sub { + my $block = shift; + if (!defined $block->main_config) { + $block->set_value("main_config", <<_EOC_ + wasm { + shm_kv kv 16k; + shm_queue q 16k; + } +_EOC_ + ); + } +}); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: shm - get_keys() default limit +--- valgrind +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.kv:set("k1", "value") + shm.kv:set("k2", "value") + + for _, k in ipairs(shm.kv:get_keys()) do + ngx.say(k) + end + } + } +--- response_body +k1 +k2 +--- no_error_log +[error] +[crit] + + + +=== TEST 2: shm - get_keys() with limit +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.kv:set("k1", "value") + shm.kv:set("k2", "value") + + local limit = 1 + + for _, k in ipairs(shm.kv:get_keys(limit)) do + ngx.say(k) + end + } + } +--- response_body +k1 +--- no_error_log +[error] +[crit] + + + +=== TEST 3: shm - get_keys() on empty shm +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + ngx.say(#shm.kv:get_keys()) + ngx.say(#shm.kv:get_keys(0)) + ngx.say(#shm.kv:get_keys(1)) + } + } +--- response_body +0 +0 +0 +--- no_error_log +[error] +[crit] + + + +=== TEST 4: shm - get_keys() bad args +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local _, perr = pcall(shm.kv.get_keys, {}, false) + ngx.say(perr) + + _, perr = pcall(shm.kv.get_keys, {}, -1) + ngx.say(perr) + } + } +--- response_body +max_count must be a number +max_count must be >= 0 +--- no_error_log +[crit] +[emerg] diff --git a/t/04-openresty/ffi/shm/020-metrics_define.t b/t/04-openresty/ffi/shm/020-metrics_define.t new file mode 100644 index 000000000..a6d842449 --- /dev/null +++ b/t/04-openresty/ffi/shm/020-metrics_define.t @@ -0,0 +1,128 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(6); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - define() sanity +--- skip_no_debug +--- valgrind +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local metrics = { + c1 = shm.metrics:define("c1", shm.metrics.COUNTER), + g1 = shm.metrics:define("g1", shm.metrics.GAUGE), + h1 = shm.metrics:define("h1", shm.metrics.HISTOGRAM), + } + + for _, id in pairs(metrics) do + assert(id > 0) + end + + ngx.say("ok") + } + } +--- response_body +ok +--- error_log eval +[ + qr/.*? \[debug\] .*? defined counter "lua.c1" with id \d+/, + qr/.*? \[debug\] .*? defined gauge "lua.g1" with id \d+/, + qr/.*? \[debug\] .*? defined histogram "lua.h1" with id \d+/, +] +--- no_error_log +[error] + + + +=== TEST 2: shm_metrics - define() name too long +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local mid, err = shm.metrics:define(string.rep(".", 500), shm.metrics.HISTOGRAM) + ngx.say("mid: ", mid) + ngx.say("err: ", err) + } + } +--- response_body +mid: nil +err: name too long +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 3: shm_metrics - define() no memory +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + for i = 1, 100 do + local _, err = shm.metrics:define(string.rep(i, 100), shm.metrics.HISTOGRAM) + if err then + ngx.log(ngx.ERR, err) + break + end + end + + ngx.say("ok") + } + } +--- response_body +ok +--- error_log eval +[ + qr/\[crit\] .*? "metrics" shm store: no memory; cannot allocate pair with key size 204 and value size 24/, + qr/\[error\] .*? access_by_lua\(.*?\):\d+: no memory/, +] +--- no_error_log +[emerg] +[alert] + + + +=== TEST 4: shm_metrics - define() bad args +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local _, perr = pcall(shm.metrics.define, {}, false, 10) + ngx.say(perr) + + _, perr = pcall(shm.metrics.define, {}, "", 10) + ngx.say(perr) + + _, perr = pcall(shm.metrics.define, {}, "c1", 10) + ngx.say(perr) + } + } +--- response_body +name must be a non-empty string +name must be a non-empty string +metric_type must be one of resty.wasmx.shm.metrics.COUNTER, resty.wasmx.shm.metrics.GAUGE, or resty.wasmx.shm.metrics.HISTOGRAM +--- no_error_log +[error] +[crit] +[emerg] +[alert] diff --git a/t/04-openresty/ffi/shm/021-metrics_increment.t b/t/04-openresty/ffi/shm/021-metrics_increment.t new file mode 100644 index 000000000..36cfd4eba --- /dev/null +++ b/t/04-openresty/ffi/shm/021-metrics_increment.t @@ -0,0 +1,83 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - increment() sanity +--- valgrind +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + local pretty = require "pl.pretty" + + local c1 = shm.metrics:define("c1", shm.metrics.COUNTER) + + shm.metrics:increment(c1) -- value defaults to 1 + shm.metrics:increment(c1, 10) + + ngx.say("c1: ", pretty.write(shm.metrics:get(c1), "")) + } + } +--- response_body +c1: {type="counter",value=11} +--- no_error_log +[error] +[crit] + + + +=== TEST 2: shm_metrics - increment() metric not found +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local ok, err = shm.metrics:increment(1, 10) + ngx.say("ok: ", ok) + ngx.say("err: ", err) + } + } +--- response_body +ok: nil +err: metric not found +--- no_error_log +[error] +[crit] + + + +=== TEST 3: shm_metrics - increment() bad args +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local _, perr = pcall(shm.metrics.increment, {}, false) + ngx.say(perr) + + _, perr = pcall(shm.metrics.increment, {}, 1, false) + ngx.say(perr) + + _, perr = pcall(shm.metrics.increment, {}, 1, 0) + ngx.say(perr) + } + } +--- response_body +metric_id must be a number +value must be > 0 +value must be > 0 +--- no_error_log +[error] +[crit] diff --git a/t/04-openresty/ffi/shm/022-metrics_record.t b/t/04-openresty/ffi/shm/022-metrics_record.t new file mode 100644 index 000000000..ce36bdd6d --- /dev/null +++ b/t/04-openresty/ffi/shm/022-metrics_record.t @@ -0,0 +1,82 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - record() sanity +--- valgrind +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + local pretty = require "pl.pretty" + + local g1 = shm.metrics:define("g1", shm.metrics.GAUGE) + local h1 = shm.metrics:define("h1", shm.metrics.HISTOGRAM) + + assert(shm.metrics:record(g1, 10)) + assert(shm.metrics:record(h1, 100)) + + ngx.say("g1: ", pretty.write(shm.metrics:get(g1), "")) + ngx.say("h1: ", pretty.write(shm.metrics:get(h1), "")) + } + } +--- response_body +g1: {type="gauge",value=10} +h1: {type="histogram",value={{count=1,ub=128},{count=0,ub=4294967295}}} +--- no_error_log +[error] +[crit] + + + +=== TEST 2: shm_metrics - record() metric not found +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local ok, err = shm.metrics:record(1, 10) + ngx.say("ok: ", ok) + ngx.say("err: ", err) + } + } +--- response_body +ok: nil +err: metric not found +--- no_error_log +[error] +[crit] + + + +=== TEST 3: shm_metrics - record() bad args +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local _, perr = pcall(shm.metrics.record, {}, false) + ngx.say(perr) + + _, perr = pcall(shm.metrics.record, {}, 1, false) + ngx.say(perr) + } + } +--- response_body +metric_id must be a number +value must be a number +--- no_error_log +[error] +[crit] diff --git a/t/04-openresty/ffi/shm/023-metrics_get.t b/t/04-openresty/ffi/shm/023-metrics_get.t new file mode 100644 index 000000000..2af9e6469 --- /dev/null +++ b/t/04-openresty/ffi/shm/023-metrics_get.t @@ -0,0 +1,82 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - get() sanity +--- valgrind +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + local pretty = require "pl.pretty" + + local c1 = shm.metrics:define("c1", shm.metrics.COUNTER) + local g1 = shm.metrics:define("g1", shm.metrics.GAUGE) + local h1 = shm.metrics:define("h1", shm.metrics.HISTOGRAM) + + shm.metrics:increment(c1) + shm.metrics:record(g1, 10) + shm.metrics:record(h1, 100) + + ngx.say("c1: ", pretty.write(shm.metrics:get(c1), "")) + ngx.say("g1: ", pretty.write(shm.metrics:get(g1), "")) + ngx.say("h1: ", pretty.write(shm.metrics:get(h1), "")) + } + } +--- response_body +c1: {type="counter",value=1} +g1: {type="gauge",value=10} +h1: {type="histogram",value={{count=1,ub=128},{count=0,ub=4294967295}}} +--- no_error_log +[error] +[crit] + + + +=== TEST 2: shm_metrics - get() metric not found +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local m = shm.metrics:get(99) + assert(m == nil) + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] + + + +=== TEST 3: shm_metrics - get() bad args +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local _, perr = pcall(shm.metrics.get, {}, false) + ngx.say(perr) + } + } +--- response_body +metric_id must be a number +--- no_error_log +[error] +[crit] diff --git a/t/04-openresty/ffi/shm/024-metrics_get_by_name.t b/t/04-openresty/ffi/shm/024-metrics_get_by_name.t new file mode 100644 index 000000000..af144097d --- /dev/null +++ b/t/04-openresty/ffi/shm/024-metrics_get_by_name.t @@ -0,0 +1,136 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - get_by_name() sanity FFI-defined metrics +prefix: lua.* +--- valgrind +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + local pretty = require "pl.pretty" + + local c1 = shm.metrics:define("c1", shm.metrics.COUNTER) + local g1 = shm.metrics:define("g1", shm.metrics.GAUGE) + local h1 = shm.metrics:define("h1", shm.metrics.HISTOGRAM) + + shm.metrics:increment(c1) + shm.metrics:record(g1, 10) + shm.metrics:record(h1, 100) + + ngx.say("c1: ", pretty.write(shm.metrics:get_by_name("c1"), "")) + ngx.say("g1: ", pretty.write(shm.metrics:get_by_name("g1"), "")) + ngx.say("h1: ", pretty.write(shm.metrics:get_by_name("h1"), "")) + } + } +--- response_body +c1: {type="counter",value=1} +g1: {type="gauge",value=10} +h1: {type="histogram",value={{count=1,ub=128},{count=0,ub=4294967295}}} +--- no_error_log +[error] +[crit] + + + +=== TEST 2: shm_metrics - get_by_name() sanity non-FFI-defined metrics +prefix: pw.hostcalls.* +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'on_configure=define_metrics \ + on=request_headers \ + test=/t/metrics/increment_counters \ + metrics=c1 \ + n_increments=13'; + + proxy_wasm hostcalls 'on_configure=define_metrics \ + on=request_headers \ + test=/t/metrics/toggle_gauges \ + metrics=g1'; + + proxy_wasm hostcalls 'on_configure=define_metrics \ + on=request_headers \ + test=/t/metrics/record_histograms \ + metrics=h1 \ + value=100'; + + content_by_lua_block { + local shm = require "resty.wasmx.shm" + local pretty = require "pl.pretty" + + ngx.say("c1: ", pretty.write(shm.metrics:get_by_name("pw.hostcalls.c1", { prefix = false }), "")) + ngx.say("g1: ", pretty.write(shm.metrics:get_by_name("pw.hostcalls.g1", { prefix = false }), "")) + ngx.say("h1: ", pretty.write(shm.metrics:get_by_name("pw.hostcalls.h1", { prefix = false }), "")) + } + } +--- response_body +c1: {type="counter",value=13} +g1: {type="gauge",value=1} +h1: {type="histogram",value={{count=1,ub=128},{count=0,ub=4294967295}}} +--- no_error_log +[error] +[crit] + + + +=== TEST 3: shm_metrics - get_by_name() metric not found +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local m = shm.metrics:get_by_name("no_such_metric") + assert(m == nil) + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] + + + +=== TEST 4: shm_metrics - get_by_name() bad args +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local _, perr = pcall(shm.metrics.get_by_name, {}, false) + ngx.say(perr) + + _, perr = pcall(shm.metrics.get_by_name, {}, "") + ngx.say(perr) + + _, perr = pcall(shm.metrics.get_by_name, {}, "m", false) + ngx.say(perr) + + _, perr = pcall(shm.metrics.get_by_name, {}, "m", { prefix = 1 }) + ngx.say(perr) + } + } +--- response_body +name must be a non-empty string +name must be a non-empty string +opts must be a table +opts.prefix must be a boolean +--- no_error_log +[error] +[crit] diff --git a/t/04-openresty/ffi/shm/025-metrics_iterate_keys.t b/t/04-openresty/ffi/shm/025-metrics_iterate_keys.t new file mode 100644 index 000000000..e0cf4b3cd --- /dev/null +++ b/t/04-openresty/ffi/shm/025-metrics_iterate_keys.t @@ -0,0 +1,40 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - iterate_keys() sanity +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local c1 = shm.metrics:define("c1", shm.metrics.COUNTER) + local g1 = shm.metrics:define("g1", shm.metrics.GAUGE) + local h1 = shm.metrics:define("h1", shm.metrics.HISTOGRAM) + + shm.metrics:lock() + + for key in shm.metrics:iterate_keys() do + ngx.say(key) + end + + shm.metrics:unlock() + } + } +--- response_body +lua.c1 +lua.g1 +lua.h1 +--- no_error_log +[error] +[crit] diff --git a/t/04-openresty/ffi/shm/026-metrics_get_keys.t b/t/04-openresty/ffi/shm/026-metrics_get_keys.t new file mode 100644 index 000000000..69513503f --- /dev/null +++ b/t/04-openresty/ffi/shm/026-metrics_get_keys.t @@ -0,0 +1,38 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(4); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - get_keys() sanity +--- metrics: 16k +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local c1 = shm.metrics:define("c1", shm.metrics.COUNTER) + local g1 = shm.metrics:define("g1", shm.metrics.GAUGE) + local h1 = shm.metrics:define("h1", shm.metrics.HISTOGRAM) + + local keys = assert(shm.metrics:get_keys()) + + for _, key in ipairs(keys) do + ngx.say(key) + end + } + } +--- response_body +lua.c1 +lua.g1 +lua.h1 +--- no_error_log +[error] +[crit] diff --git a/t/lib/proxy-wasm-tests/hostcalls/src/tests/mod.rs b/t/lib/proxy-wasm-tests/hostcalls/src/tests/mod.rs index 17acfb3c3..82993ef99 100644 --- a/t/lib/proxy-wasm-tests/hostcalls/src/tests/mod.rs +++ b/t/lib/proxy-wasm-tests/hostcalls/src/tests/mod.rs @@ -125,7 +125,9 @@ pub(crate) fn test_log_properties(ctx: &(dyn TestContext + 'static), phase: Test pub(crate) fn test_log_metrics(ctx: &(dyn TestContext + 'static), phase: TestPhase) { for (n, id) in ctx.get_metrics_mapping() { - if n.starts_with('h') { continue } + if n.starts_with('h') { + continue; + } let value = get_metric(*id).unwrap(); info!("{}: {} at {:?}", n, value, phase) } @@ -393,6 +395,21 @@ pub(crate) fn test_get_shared_data(ctx: &TestHttp) { ); } +pub(crate) fn test_log_shared_data(ctx: &TestHttp) { + let key = ctx.config.get("key").unwrap(); + let (data, cas) = ctx.get_shared_data(key); + let cas_value = cas + .map(|x| format!("{x}")) + .unwrap_or_else(|| "0".to_string()); + + info!( + "{}: {:?} {}", + key, + std::str::from_utf8(data.as_deref().unwrap_or_default()).unwrap(), + cas_value.as_str() + ) +} + pub(crate) fn test_set_shared_data(ctx: &TestHttp) { let cas = ctx.config.get("cas").map(|x| x.parse::().unwrap()); @@ -478,7 +495,11 @@ pub(crate) fn test_define_metrics(ctx: &mut (dyn TestContext + 'static)) { } } -pub(crate) fn test_increment_counters(ctx: &(dyn TestContext + 'static), phase: TestPhase, skip_others: Option) { +pub(crate) fn test_increment_counters( + ctx: &(dyn TestContext + 'static), + phase: TestPhase, + skip_others: Option, +) { if !should_run_on_current_worker(ctx) { return; } @@ -488,7 +509,9 @@ pub(crate) fn test_increment_counters(ctx: &(dyn TestContext + 'static), phase: .map_or(1, |x| x.parse::().expect("bad n_increments value")); for (n, id) in ctx.get_metrics_mapping() { - if skip_others.unwrap_or(true) && !n.starts_with('c') { continue } + if skip_others.unwrap_or(true) && !n.starts_with('c') { + continue; + } for _ in 0..n_increments { increment_metric(*id, 1).unwrap(); @@ -500,13 +523,19 @@ pub(crate) fn test_increment_counters(ctx: &(dyn TestContext + 'static), phase: test_log_metrics(ctx, phase); } -pub(crate) fn test_toggle_gauges(ctx: &(dyn TestContext + 'static), phase: TestPhase, skip_others: Option) { +pub(crate) fn test_toggle_gauges( + ctx: &(dyn TestContext + 'static), + phase: TestPhase, + skip_others: Option, +) { if !should_run_on_current_worker(ctx) { return; } for (n, id) in ctx.get_metrics_mapping() { - if skip_others.unwrap_or(true) && !n.starts_with('g') { continue } + if skip_others.unwrap_or(true) && !n.starts_with('g') { + continue; + } let new_value = match get_metric(*id).unwrap() { 0 => 1, @@ -530,7 +559,9 @@ pub(crate) fn test_record_metric(ctx: &(dyn TestContext + 'static), phase: TestP .map_or(1, |x| x.parse::().expect("bad value")); for (n, id) in ctx.get_metrics_mapping() { - if n.starts_with('c') { continue } + if n.starts_with('c') { + continue; + } record_metric(*id, value).unwrap(); info!("record {} on {} at {:?}", value, n, phase); } @@ -540,7 +571,9 @@ pub(crate) fn test_record_metric(ctx: &(dyn TestContext + 'static), phase: TestP pub(crate) fn test_get_metrics(ctx: &TestHttp) { for (n, id) in ctx.get_metrics_mapping() { - if n.starts_with('h') { continue } + if n.starts_with('h') { + continue; + } let name = n.replace("_", "-"); let value = get_metric(*id).unwrap().to_string(); diff --git a/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs b/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs index a097f0415..5240f512e 100644 --- a/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs +++ b/t/lib/proxy-wasm-tests/hostcalls/src/types/test_http.rs @@ -118,6 +118,7 @@ impl TestHttp { /* shared memory */ "/t/shm/get_shared_data" => test_get_shared_data(self), + "/t/shm/log_shared_data" => test_log_shared_data(self), "/t/shm/set_shared_data" => test_set_shared_data(self), "/t/shm/set_shared_data_by_len" => test_set_shared_data_by_len(self), "/t/shm/enqueue" => test_shared_queue_enqueue(self), @@ -144,7 +145,8 @@ impl TestHttp { } "/t/metrics/get_histogram" => { info!("retrieving histogram in \"{:?}\"", cur_phase); - let h_id = define_metric(MetricType::Histogram, "h1").expect("cannot define new metric"); + let h_id = + define_metric(MetricType::Histogram, "h1").expect("cannot define new metric"); get_metric(h_id).unwrap(); } diff --git a/util/_lib.sh b/util/_lib.sh index 0f796c12e..e60bd069a 100644 --- a/util/_lib.sh +++ b/util/_lib.sh @@ -74,6 +74,7 @@ install_luarocks() { $BIN_LUAROCKS --tree=$DIR_LUAJIT --lua-version=5.1 install lua-resty-dns-client $BIN_LUAROCKS --tree=$DIR_LUAJIT --lua-version=5.1 install LuaFileSystem + $BIN_LUAROCKS --tree=$DIR_LUAJIT --lua-version=5.1 install penlight } remove_luarocks() {