diff --git a/assets/INTERNAL_DIFFERENCES.md b/assets/INTERNAL_DIFFERENCES.md new file mode 100644 index 000000000..8ccec2ed6 --- /dev/null +++ b/assets/INTERNAL_DIFFERENCES.md @@ -0,0 +1,36 @@ +# Host SDK implementation differences + +This document lists internal discrepancies between ngx_wasm_module and other +Proxy-Wasm host SDK implementations, e.g. Envoy, and it's meant to be used by +ngx_wasm_module's developers rather than its users. + +## Table of Contents + +- [Metrics Prefixing](#metrics-prefixing) + +## Metrics Prefixing + +In ngx_wasm_module, Proxy-Wasm metric names are prefixed with `pw.filter_name.`, +where `filter_name` represents the name assigned to the filter in which the +metric is defined. A metric `m1` defined by a filter `f1` is represented +internally as `pw.f1.m1`. + +Envoy, however, prefixes Proxy-Wasm metric names `wasmcustom.`. As such, a +metric `m1` defined by a filter `f1` is represented internally as +`wasmcustom.m1`. + +Proxy-Wasm filter developers don't need to be aware of such divergence as they +never use the metric name to retrieve or update a metric's value. + +In fact, this difference shouldn't affect users at all, as long as host +implementations expose metric names without their internal prefixing. + +Although Envoy will expose its internal prefixing in responses to the +`/metrics` admin endpoint, it won't do it in responses to the +`/metrics/prometheus` admin endpoint. + +ngx_wasm_module follows a similar pattern. Although its internal prefixing is +exposed in metric names retrieved by its FFI, this interface is intended to be +accessed by Kong Gateway developers only, not end-users. Kong Gateway in turn +strips the internal prefixing before serializing the metrics in responses to the +`/metrics` admin endpoint. diff --git a/config b/config index e1bec6fe8..3c8e10dc9 100644 --- a/config +++ b/config @@ -140,11 +140,10 @@ NGX_WASMX_DEPS="\ $ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm.h \ $ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_maps.h \ $ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_properties.h \ - $ngx_addon_dir/src/common/shm/ngx_wasm_shm.h \ - $ngx_addon_dir/src/common/shm/ngx_wasm_shm_kv.h \ - $ngx_addon_dir/src/common/shm/ngx_wasm_shm_queue.h \ - $ngx_addon_dir/src/common/metrics/ngx_wa_metrics.h \ - $ngx_addon_dir/src/common/metrics/ngx_wa_histogram.h" + $ngx_addon_dir/src/common/shm/ngx_wa_shm.h \ + $ngx_addon_dir/src/common/shm/ngx_wa_shm_kv.h \ + $ngx_addon_dir/src/common/shm/ngx_wa_shm_queue.h \ + $ngx_addon_dir/src/common/metrics/ngx_wa_metrics.h" NGX_WASMX_SRCS="\ $ngx_addon_dir/src/ngx_wasmx.c \ @@ -156,9 +155,9 @@ NGX_WASMX_SRCS="\ $ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_maps.c \ $ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_properties.c \ $ngx_addon_dir/src/common/proxy_wasm/ngx_proxy_wasm_util.c \ - $ngx_addon_dir/src/common/shm/ngx_wasm_shm.c \ - $ngx_addon_dir/src/common/shm/ngx_wasm_shm_kv.c \ - $ngx_addon_dir/src/common/shm/ngx_wasm_shm_queue.c \ + $ngx_addon_dir/src/common/shm/ngx_wa_shm.c \ + $ngx_addon_dir/src/common/shm/ngx_wa_shm_kv.c \ + $ngx_addon_dir/src/common/shm/ngx_wa_shm_queue.c \ $ngx_addon_dir/src/common/metrics/ngx_wa_metrics.c \ $ngx_addon_dir/src/common/metrics/ngx_wa_histogram.c" diff --git a/lib/resty/wasmx/shm.lua b/lib/resty/wasmx/shm.lua new file mode 100644 index 000000000..d62a2c26f --- /dev/null +++ b/lib/resty/wasmx/shm.lua @@ -0,0 +1,515 @@ +-- 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 table_new = table.new +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_log = ngx.log +local ngx_debug = ngx.DEBUG +local ngx_warn = ngx.WARN +local ngx_sleep = ngx.sleep +local FFI_ABORT = wasmx.FFI_ABORT +local FFI_DECLINED = wasmx.FFI_DECLINED +local FFI_ERROR = wasmx.FFI_ERROR +local FFI_OK = wasmx.FFI_OK + + +ffi.cdef [[ + typedef unsigned char u_char; + typedef uintptr_t ngx_uint_t; + typedef ngx_uint_t ngx_msec_t; + typedef struct ngx_log_s ngx_log_t; + 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); + + + int ngx_wa_ffi_shm_setup_zones(ngx_wa_ffi_shm_setup_zones_handler handler); + int ngx_wa_ffi_shm_iterate_keys(ngx_wa_shm_t *shm, + ngx_uint_t page, + ngx_uint_t page_size, + ngx_str_t **keys, + ngx_uint_t *total); + void ngx_wa_ffi_shm_lock(ngx_wa_shm_t *shm); + void ngx_wa_ffi_shm_unlock(ngx_wa_shm_t *shm); + + int ngx_wa_ffi_shm_get_kv_value(ngx_wa_shm_t *shm, + ngx_str_t *k, + ngx_str_t **v, + uint32_t *cas); + int ngx_wa_ffi_shm_set_kv_value(ngx_wa_shm_t *shm, + ngx_str_t *k, + ngx_str_t *v, + uint32_t cas, + int *written); + + int ngx_wa_ffi_shm_define_metric(ngx_str_t *name, + ngx_wa_metric_type_e type, + uint32_t *metric_id); + int ngx_wa_ffi_shm_record_metric(uint32_t metric_id, int value); + int ngx_wa_ffi_shm_increment_metric(uint32_t metric_id, int value); + int ngx_wa_ffi_shm_get_metric(uint32_t metric_id, + ngx_str_t *name, + u_char *mbuf, size_t mbs, + u_char *hbuf, size_t hbs); + + int ngx_wa_ffi_shm_one_slot_metric_size(); + int ngx_wa_ffi_shm_max_histogram_size(); +]] + + +local WASM_SHM_KEY = {} +local DEFAULT_KEY_BATCH_SIZE = 500 + + +local _M = {} + + +local _initialized = false +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 _kbuf = ffi_new("ngx_str_t *[?]", DEFAULT_KEY_BATCH_SIZE) +local _mbs = C.ngx_wa_ffi_shm_one_slot_metric_size() +local _mbuf = ffi_new("u_char [?]", _mbs) +local _hbs = C.ngx_wa_ffi_shm_max_histogram_size() +local _hbuf = ffi_new("u_char [?]", _hbs) + + +local function lock_shm(zone) + C.ngx_wa_ffi_shm_lock(zone[WASM_SHM_KEY]) +end + + +local function unlock_shm(zone) + C.ngx_wa_ffi_shm_unlock(zone[WASM_SHM_KEY]) +end + + +local function key_iterator(ctx) + if ctx.i < ctx.page_total then + local ckey = ctx.ckeys[ctx.i] + ctx.i = ctx.i + 1 + + return ffi_str(ckey.data, ckey.len) + + else + ctx.cpage_total[0] = 0 + + local rc = C.ngx_wa_ffi_shm_iterate_keys(ctx.shm, + ctx.page, ctx.page_size, + ctx.ckeys, ctx.cpage_total) + if rc == FFI_ABORT then + local zone_name = ffi_str(ctx.shm.name.data, ctx.shm.name.len) + local err = "attempt to iterate over the keys of an unlocked shm zone." + .. " please call resty.wasmx.shm.%s:lock() before calling" + .. " iterate_keys() and resty.wasmx.shm.%s:unlock() after" + + error(str_fmt(err, zone_name, zone_name), 2) + end + + if rc == FFI_DECLINED then + return + end + + ngx_sleep(0) + + ctx.page_total = tonumber(ctx.cpage_total[0]) + ctx.page = ctx.page + ctx.page_total + ctx.i = 1 + + return ffi_str(ctx.ckeys[0].data, ctx.ckeys[0].len) + end +end + + +local function iterate_keys(zone, page_size) + if page_size ~= nil and type(page_size) ~= "number" then + error("page_size must be a number", 2) + end + + local ctx = { + shm = zone[WASM_SHM_KEY], + i = 0, + page = 0, + page_size = page_size and page_size or DEFAULT_KEY_BATCH_SIZE, + page_total = 0, + cpage_total = 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 get_keys(zone, max) + if max ~= nil and type(max) ~= "number" then + error("max must be number", 2) + end + + local shm = zone[WASM_SHM_KEY] + local nkeys + local keys + local start = 0 + local ctotal = ffi_new("ngx_uint_t [1]") + + lock_shm(zone) + + if max == 0 then + local rc = C.ngx_wa_ffi_shm_iterate_keys(shm, start, 0, nil, ctotal) + nkeys = tonumber(ctotal[0]) + + if rc == FFI_DECLINED then + unlock_shm(zone) + return {} + end + + else + nkeys = max and max or DEFAULT_KEY_BATCH_SIZE + end + + local ckeys = ffi_new("ngx_str_t *[?]", nkeys) + + C.ngx_wa_ffi_shm_iterate_keys(shm, start, nkeys, ckeys, ctotal) + + unlock_shm(zone) + + local total = tonumber(ctotal[0]) + local keys = table_new(0, total) + + for i = 1, total do + local ckey = ckeys[i - 1] + keys[i] = ffi_str(ckey.data, ckey.len) + end + + return keys +end + + +local function get_kv_value(zone, key) + if type(key) ~= "string" then + error("key must be 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_get_kv_value(shm, cname, cvalue, ccas) + if rc == FFI_DECLINED then + return nil, nil, "key not found" + end + + return ffi_str(cvalue[0].data, cvalue[0].len), tonumber(ccas[0]) +end + + +local function set_kv_value(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 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("uint32_t [1]") + + local rc = C.ngx_wa_ffi_shm_set_kv_value(shm, cname, cvalue, cas, written) + if rc == FFI_ERR then + return nil, "failed setting kv value, no memory" + end + + return tonumber(written[0]) +end + + +local function parse_cmetric(cmetric) + if cmetric.metric_type == _types.ffi_metric.COUNTER then + return { type = "counter", value = tonumber(cmetric.slots[0].counter) } + + elseif cmetric.metric_type == _types.ffi_metric.GAUGE then + return { type = "gauge", value = tonumber(cmetric.slots[0].gauge.value) } + + elseif 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 + + h.value[#(h.value) + 1] = { ub = cb.upper_bound, count = cb.count } + end + + return h + end +end + + +local function get_metric(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_get_metric(metric_id, nil, + _mbuf, _mbs, _hbuf, _hbs) + if rc == FFI_DECLINED then + return nil, "metric not found" + end + + 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.metric, lua.metric or wa.metric. +-- +-- get_metric_by_name assumes that it's retrieving a lua land metric and +-- will by default prefix name with `lua.` +-- +-- This behavior can be disabled by passing `opts.prefix` as false. +--- +local function get_metric_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 + + name = (opts and opts.prefix == false) and name or "lua." .. name + local cname = ffi_new("ngx_str_t", { data = name, len = #name }) + + ffi_fill(_mbuf, _mbs) + ffi_fill(_hbuf, _hbs) + + local rc = C.ngx_wa_ffi_shm_get_metric(0, cname, _mbuf, _mbs, _hbuf, _hbs) + if rc == FFI_DECLINED then + return nil, "metric not found" + end + + return parse_cmetric(ffi_cast("ngx_wa_metric_t *", _mbuf)) +end + + +local function define_metric(zone, name, metric_type) + if type(name) ~= "string" or name == "" then + error("name must be a non-empty string", 2) + end + + if _metric_type_set[metric_type] == nil then + local err = "metric_type must be either" + .. " resty.wasmx.shm.metrics.COUNTER," + .. " resty.wasmx.shm.metrics.GAUGE, or" + .. " resty.wasmx.shm.metrics.HISTOGRAM" + + error(err, 2) + end + + 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_define_metric(cname, metric_type, m_id) + if rc == FFI_ABORT then + return nil, "failed defining metric, name too long" + end + + if rc == FFI_ERROR then + return nil, "failed defining metric, no memory" + end + + return tonumber(m_id[0]) +end + + +local function record_metric(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_record_metric(metric_id, value) + if rc == FFI_DECLINED then + return nil, "metric not found" + end + + return true +end + + +local function increment_metric(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 a number greater than zero", 2) + end + + value = value and value or 1 + + local rc = C.ngx_wa_ffi_shm_increment_metric(metric_id, value) + if rc == FFI_DECLINED then + return nil, "metric not found" + end + + return true +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 = lock_shm, + unlock = unlock_shm + } + + if shm.type == _types.ffi_shm.SHM_TYPE_KV then + _M[zone_name].get_keys = get_keys + _M[zone_name].iterate_keys = iterate_keys + _M[zone_name].get = get_kv_value + _M[zone_name].set = set_kv_value + + 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 = get_keys + _M[zone_name].iterate_keys = iterate_keys + _M[zone_name].get = get_metric + _M[zone_name].get_by_name = get_metric_by_name + _M[zone_name].define = define_metric + _M[zone_name].increment = increment_metric + _M[zone_name].record = record_metric + _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) + + +function _M.init() + if _initialized then + return + end + + local rc = C.ngx_wa_ffi_shm_setup_zones(_setup_zones_handler) + if rc == FFI_ABORT then + ngx_log(ngx.DEBUG, "no shm zones found for resty.wasmx interface") + end + + _initialized = true +end + + +_M.init() + + +return _M diff --git a/src/common/lua/ngx_wasm_lua_ffi.c b/src/common/lua/ngx_wasm_lua_ffi.c index 1c24ae600..5bf2936f8 100644 --- a/src/common/lua/ngx_wasm_lua_ffi.c +++ b/src/common/lua/ngx_wasm_lua_ffi.c @@ -4,6 +4,7 @@ #include "ddebug.h" #include +#include #include @@ -257,3 +258,218 @@ 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 + + +void +ngx_wa_ffi_shm_lock(ngx_wa_shm_t *shm) +{ + ngx_wa_assert(shm); + + ngx_wa_shm_lock(shm); +} + + +void +ngx_wa_ffi_shm_unlock(ngx_wa_shm_t *shm) +{ + ngx_wa_assert(shm); + + ngx_wa_shm_unlock(shm); +} + + +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; + + ngx_wa_assert(setup_handler); + + 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 +shm_kv_retrieve_keys(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel, + ngx_uint_t start, ngx_uint_t limit, + ngx_str_t **keys, ngx_uint_t *total) +{ + ngx_wa_shm_kv_node_t *n = (ngx_wa_shm_kv_node_t *) node; + + if (limit > 0 && (*total - start) == limit) { + return; + } + + if (keys && *total >= start) { + keys[(*total - start)] = &n->key.str; + } + + (*total)++; + + if (node->left != sentinel) { + shm_kv_retrieve_keys(node->left, sentinel, start, limit, keys, total); + } + + if (node->right != sentinel) { + shm_kv_retrieve_keys(node->right, sentinel, start, limit, keys, total); + } +} + + +ngx_int_t +ngx_wa_ffi_shm_iterate_keys(ngx_wa_shm_t *shm, + ngx_uint_t page, ngx_uint_t page_size, + ngx_str_t **keys, ngx_uint_t *total) +{ + ngx_wa_shm_kv_t *kv; + + ngx_wa_assert(shm && shm->type != NGX_WA_SHM_TYPE_QUEUE); + + if (!ngx_wa_shm_locked(shm)) { + return NGX_ABORT; + } + + kv = shm->data; + + if (page >= kv->nelts) { + return NGX_DECLINED; + } + + if (kv->rbtree.root != kv->rbtree.sentinel) { + shm_kv_retrieve_keys(kv->rbtree.root, kv->rbtree.sentinel, + page, page_size, keys, total); + } + + *total = *total - page; + + return NGX_OK; +} + + +ngx_int_t +ngx_wa_ffi_shm_get_kv_value(ngx_wa_shm_t *shm, + ngx_str_t *k, ngx_str_t **v, uint32_t *cas) +{ + unsigned unlock = 0; + ngx_int_t rc; + + ngx_wa_assert(shm && k && v && cas); + + if (!ngx_wa_shm_locked(shm)) { + 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_set_kv_value(ngx_wa_shm_t *shm, + ngx_str_t *k, ngx_str_t *v, uint32_t cas, + ngx_int_t *written) +{ + unsigned unlock = 0; + ngx_int_t rc; + + ngx_wa_assert(shm && k && v && written); + + if (!ngx_wa_shm_locked(shm)) { + ngx_wa_shm_lock(shm); + unlock = 1; + } + + rc = ngx_wa_shm_kv_set_locked(shm, k, v, cas, written); + + if (unlock) { + ngx_wa_shm_unlock(shm); + } + + return rc; +} + + +ngx_int_t +ngx_wa_ffi_shm_define_metric(ngx_str_t *name, ngx_wa_metric_type_e type, + uint32_t *metric_id) +{ + ngx_int_t rc; + ngx_str_t prefixed_name; + ngx_wa_metrics_t *metrics = ngx_wasmx_metrics((ngx_cycle_t *) ngx_cycle); + u_char buf[NGX_MAX_ERROR_STR]; + + ngx_wa_assert(metrics && name); + + prefixed_name.data = buf; + prefixed_name.len = ngx_sprintf(buf, "lua.%V", name) - buf; + + rc = ngx_wa_metrics_define(metrics, &prefixed_name, type, metric_id); + if (rc != NGX_OK) { + return rc; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_wa_ffi_shm_increment_metric(uint32_t metric_id, ngx_uint_t value) +{ + ngx_wa_metrics_t *metrics = ngx_wasmx_metrics((ngx_cycle_t *) ngx_cycle); + + ngx_wa_assert(metrics); + + return ngx_wa_metrics_increment(metrics, metric_id, value); +} + + +ngx_int_t +ngx_wa_ffi_shm_record_metric(uint32_t metric_id, ngx_uint_t value) +{ + ngx_wa_metrics_t *metrics = ngx_wasmx_metrics((ngx_cycle_t *) ngx_cycle); + + ngx_wa_assert(metrics); + + return ngx_wa_metrics_record(metrics, metric_id, value); +} + + +ngx_int_t +ngx_wa_ffi_shm_get_metric(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; + + ngx_wa_assert(metrics && (name || metric_id) && m_buf && h_buf); + + 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..f96d31ed1 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,39 @@ 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, + ngx_uint_t page_size, ngx_str_t **keys, ngx_uint_t *total); +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_get_kv_value(ngx_wa_shm_t *shm, ngx_str_t *k, + ngx_str_t **v, uint32_t *cas); +ngx_int_t ngx_wa_ffi_shm_set_kv_value(ngx_wa_shm_t *shm, ngx_str_t *k, + ngx_str_t *v, uint32_t cas, ngx_int_t *written); + + +ngx_int_t ngx_wa_ffi_shm_define_metric(ngx_str_t *name, + ngx_wa_metric_type_e type, uint32_t *metric_id); +ngx_int_t ngx_wa_ffi_shm_increment_metric(uint32_t metric_id, ngx_uint_t value); +ngx_int_t ngx_wa_ffi_shm_record_metric(uint32_t metric_id, ngx_uint_t value); +ngx_int_t ngx_wa_ffi_shm_get_metric(uint32_t metric_id, ngx_str_t *name, + u_char *m_buf, size_t mbs, u_char *h_buf, size_t hbs); + + +ngx_int_t +ngx_wa_ffi_shm_one_slot_metric_size() +{ + return NGX_WA_METRICS_ONE_SLOT_METRIC_SIZE; +} + + +ngx_int_t +ngx_wa_ffi_shm_max_histogram_size() +{ + return NGX_WA_METRICS_MAX_HISTOGRAM_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 b21dd385e..d52e46cef 100644 --- a/src/common/metrics/ngx_wa_histogram.c +++ b/src/common/metrics/ngx_wa_histogram.c @@ -4,12 +4,7 @@ #include "ddebug.h" #include -#include - - -#define NGX_WA_BINS_INIT 5 -#define NGX_WA_BINS_MAX 18 -#define NGX_WA_BINS_INCREMENT 4 +#include static uint32_t @@ -40,20 +35,21 @@ histogram_grow(ngx_wa_metrics_t *metrics, ngx_wa_metrics_histogram_t *h, ngx_uint_t n; ngx_wa_metrics_histogram_t *new_h = NULL; - if (h->n_bins == NGX_WA_BINS_MAX) { + if (h->n_bins == NGX_WA_METRICS_BINS_MAX) { return NGX_ERROR; } ngx_log_debug0(NGX_LOG_DEBUG_WASM, metrics->shm->log, 0, "growing histogram"); - n = ngx_min(NGX_WA_BINS_INCREMENT, NGX_WA_BINS_MAX - h->n_bins); + n = ngx_min(NGX_WA_METRICS_BINS_INCREMENT, + NGX_WA_METRICS_BINS_MAX - h->n_bins); old_size = sizeof(ngx_wa_metrics_histogram_t) + sizeof(ngx_wa_metrics_bin_t) * h->n_bins; size = old_size + sizeof(ngx_wa_metrics_bin_t) * n; - if (metrics->shm->eviction == NGX_WASM_SHM_EVICTION_NONE) { - ngx_wasm_shm_lock(metrics->shm); + if (metrics->shm->eviction == NGX_WA_SHM_EVICTION_NONE) { + ngx_wa_shm_lock(metrics->shm); } new_h = ngx_slab_calloc_locked(metrics->shm->shpool, size); @@ -72,8 +68,8 @@ histogram_grow(ngx_wa_metrics_t *metrics, ngx_wa_metrics_histogram_t *h, error: - if (metrics->shm->eviction == NGX_WASM_SHM_EVICTION_NONE) { - ngx_wasm_shm_unlock(metrics->shm); + if (metrics->shm->eviction == NGX_WA_SHM_EVICTION_NONE) { + ngx_wa_shm_unlock(metrics->shm); } return rc; @@ -96,7 +92,8 @@ histogram_bin(ngx_wa_metrics_t *metrics, ngx_wa_metrics_histogram_t *h, return b; } - if (b->upper_bound == 0) { + if (b->upper_bound == NGX_MAX_UINT32_VALUE) { + i++; break; } } @@ -128,29 +125,29 @@ histogram_bin(ngx_wa_metrics_t *metrics, ngx_wa_metrics_histogram_t *h, static void histogram_log(ngx_wa_metrics_t *metrics, ngx_wa_metric_t *m, uint32_t mid) { - size_t i, size = sizeof(ngx_wa_metrics_histogram_t) - + sizeof(ngx_wa_metrics_bin_t) - * NGX_WA_BINS_MAX; + size_t i; + u_char *p; ngx_wa_metrics_bin_t *b; ngx_wa_metrics_histogram_t *h; - u_char *p, buf[size], s_buf[NGX_MAX_ERROR_STR]; + u_char h_buf[NGX_WA_METRICS_MAX_HISTOGRAM_SIZE]; + u_char s_buf[NGX_MAX_ERROR_STR]; - ngx_memzero(buf, size); + ngx_memzero(h_buf, sizeof(h_buf)); p = s_buf; - h = (ngx_wa_metrics_histogram_t *) buf; - h->n_bins = NGX_WA_BINS_MAX; + h = (ngx_wa_metrics_histogram_t *) h_buf; + h->n_bins = NGX_WA_METRICS_BINS_MAX; h->bins[0].upper_bound = NGX_MAX_UINT32_VALUE; ngx_wa_metrics_histogram_get(metrics, m, metrics->workers, h); for (i = 0; i < h->n_bins; i++) { b = &h->bins[i]; - if (b->upper_bound == 0) { + p = ngx_sprintf(p, " %uD: %uD;", b->upper_bound, b->count); + + if (b->upper_bound == NGX_MAX_UINT32_VALUE) { break; } - - p = ngx_sprintf(p, " %uD: %uD;", b->upper_bound, b->count); } ngx_log_debug3(NGX_LOG_DEBUG_WASM, metrics->shm->log, 0, @@ -165,7 +162,7 @@ ngx_wa_metrics_histogram_add_locked(ngx_wa_metrics_t *metrics, ngx_wa_metric_t *m) { size_t i; - static uint16_t n_bins = NGX_WA_BINS_INIT; + static uint16_t n_bins = NGX_WA_METRICS_BINS_INIT; ngx_wa_metrics_histogram_t **h; for (i = 0; i < metrics->workers; i++) { @@ -228,12 +225,12 @@ ngx_wa_metrics_histogram_get(ngx_wa_metrics_t *metrics, ngx_wa_metric_t *m, for (j = 0; j < h->n_bins; j++) { b = &h->bins[j]; - if (b->upper_bound == 0) { - break; - } - out_b = histogram_bin(metrics, out, b->upper_bound, NULL); out_b->count += b->count; + + if (b->upper_bound == NGX_MAX_UINT32_VALUE) { + break; + } } } } diff --git a/src/common/metrics/ngx_wa_histogram.h b/src/common/metrics/ngx_wa_histogram.h deleted file mode 100644 index 854b1ad57..000000000 --- a/src/common/metrics/ngx_wa_histogram.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _NGX_WA_HISTOGRAM_H_INCLUDED_ -#define _NGX_WA_HISTOGRAM_H_INCLUDED_ - - -#include - - -ngx_int_t ngx_wa_metrics_histogram_add_locked(ngx_wa_metrics_t *metrics, - ngx_wa_metric_t *m); -ngx_int_t ngx_wa_metrics_histogram_record(ngx_wa_metrics_t *metrics, - ngx_wa_metric_t *m, ngx_uint_t slot, uint32_t mid, ngx_uint_t n); -void ngx_wa_metrics_histogram_get(ngx_wa_metrics_t *metrics, ngx_wa_metric_t *m, - ngx_uint_t slots, ngx_wa_metrics_histogram_t *out); - - -#endif /* _NGX_WA_HISTOGRAM_H_INCLUDED_ */ diff --git a/src/common/metrics/ngx_wa_metrics.c b/src/common/metrics/ngx_wa_metrics.c index 754e8f470..2d8d11524 100644 --- a/src/common/metrics/ngx_wa_metrics.c +++ b/src/common/metrics/ngx_wa_metrics.c @@ -5,7 +5,6 @@ #include #include -#include ngx_str_t * @@ -74,7 +73,7 @@ realloc_histogram(ngx_wa_metrics_t *metrics, ngx_wa_metric_t *old_m, ngx_str_t *val; ngx_wa_metric_t *m; - rc = ngx_wasm_shm_kv_get_locked(metrics->shm, NULL, &mid, &val, &cas); + rc = ngx_wa_shm_kv_get_locked(metrics->shm, NULL, &mid, &val, &cas); if (rc != NGX_OK) { return rc; } @@ -91,11 +90,11 @@ static ngx_int_t realloc_metrics(ngx_wa_metrics_t *metrics, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { - uint32_t mid; - ngx_int_t rc; - ngx_uint_t val; - ngx_wasm_shm_kv_node_t *n = (ngx_wasm_shm_kv_node_t *) node; - ngx_wa_metric_t *m = (ngx_wa_metric_t *) n->value.data; + uint32_t mid; + ngx_int_t rc; + ngx_uint_t val; + ngx_wa_shm_kv_node_t *n = (ngx_wa_shm_kv_node_t *) node; + ngx_wa_metric_t *m = (ngx_wa_metric_t *) n->value.data; if (node == sentinel) { return NGX_OK; @@ -168,7 +167,7 @@ ngx_wa_metrics_alloc(ngx_cycle_t *cycle) metrics->config.slab_size = NGX_CONF_UNSET_SIZE; metrics->config.max_metric_name_length = NGX_CONF_UNSET_SIZE; - metrics->shm = ngx_pcalloc(cycle->pool, sizeof(ngx_wasm_shm_t)); + metrics->shm = ngx_pcalloc(cycle->pool, sizeof(ngx_wa_shm_t)); if (metrics->shm == NULL) { ngx_pfree(cycle->pool, metrics); return NULL; @@ -176,8 +175,8 @@ ngx_wa_metrics_alloc(ngx_cycle_t *cycle) metrics->shm->log = &cycle->new_log; metrics->shm->name = shm_name; - metrics->shm->type = NGX_WASM_SHM_TYPE_METRICS; - metrics->shm->eviction = NGX_WASM_SHM_EVICTION_NONE; + metrics->shm->type = NGX_WA_SHM_TYPE_METRICS; + metrics->shm->eviction = NGX_WA_SHM_EVICTION_NONE; return metrics; } @@ -187,8 +186,9 @@ char * ngx_wa_metrics_init_conf(ngx_wa_metrics_t *metrics, ngx_conf_t *cf) { ngx_cycle_t *cycle = cf->cycle; - ngx_wa_metrics_t *old_metrics = metrics->old_metrics; ngx_core_conf_t *ccf; + ngx_array_t *shms = ngx_wasmx_shms(cycle); + ngx_wa_metrics_t *old_metrics = metrics->old_metrics; ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); @@ -203,22 +203,29 @@ ngx_wa_metrics_init_conf(ngx_wa_metrics_t *metrics, ngx_conf_t *cf) /* TODO: if eviction is enabled, metrics->workers must be set to 1 */ metrics->workers = ccf->worker_processes; - metrics->shm_zone = ngx_shared_memory_add(cf, &metrics->shm->name, - metrics->config.slab_size, - &ngx_wasmx_module); - if (metrics->shm_zone == NULL) { + + metrics->mapping = ngx_array_push(shms); + if (metrics->mapping == NULL) { + return NULL; + } + + metrics->mapping->name = metrics->shm->name; + metrics->mapping->zone = ngx_shared_memory_add(cf, &metrics->shm->name, + metrics->config.slab_size, + &ngx_wasmx_module); + if (metrics->mapping->zone == NULL) { return NGX_CONF_ERROR; } - metrics->shm_zone->init = ngx_wasm_shm_init_zone; - metrics->shm_zone->data = metrics->shm; - metrics->shm_zone->noreuse = 0; + metrics->mapping->zone->init = ngx_wa_shm_init_zone; + metrics->mapping->zone->data = metrics->shm; + metrics->mapping->zone->noreuse = 0; if (old_metrics && (metrics->workers != old_metrics->workers || metrics->config.slab_size != old_metrics->config.slab_size)) { - metrics->shm_zone->noreuse = 1; + metrics->mapping->zone->noreuse = 1; } return NGX_CONF_OK; @@ -226,27 +233,30 @@ ngx_wa_metrics_init_conf(ngx_wa_metrics_t *metrics, ngx_conf_t *cf) ngx_int_t -ngx_wa_metrics_init(ngx_wa_metrics_t *metrics, ngx_cycle_t *cycle) +ngx_wa_metrics_init(ngx_cycle_t *cycle) { - ngx_wasm_shm_kv_t *old_shm_kv; + ngx_int_t rc; + ngx_wa_shm_kv_t *old_shm_kv; + ngx_wa_metrics_t *metrics = ngx_wasmx_metrics(cycle); - if (metrics->old_metrics && !metrics->shm_zone->noreuse) { + if (metrics->old_metrics && !metrics->mapping->zone->noreuse) { /* reuse old kv store */ metrics->shm->data = metrics->old_metrics->shm->data; return NGX_OK; } - if (ngx_wasm_shm_kv_init(metrics->shm) != NGX_OK) { - return NGX_ERROR; + rc = ngx_wa_shm_kv_init(metrics->shm); + if (rc != NGX_OK) { + return rc; } - if (metrics->old_metrics && metrics->shm_zone->noreuse) { + if (metrics->old_metrics && metrics->mapping->zone->noreuse) { /* mark the old kv store for cleanup during SIGHUP old_cycle free */ - metrics->old_metrics->shm_zone->noreuse = 1; - metrics->shm_zone->noreuse = 0; + metrics->old_metrics->mapping->zone->noreuse = 1; + metrics->mapping->zone->noreuse = 0; /* realloc old kv store */ - old_shm_kv = ngx_wasm_shm_get_kv(metrics->old_metrics->shm); + old_shm_kv = ngx_wa_shm_get_kv(metrics->old_metrics->shm); return realloc_metrics(metrics, old_shm_kv->rbtree.root, old_shm_kv->rbtree.sentinel); @@ -286,9 +296,9 @@ ngx_wa_metrics_define(ngx_wa_metrics_t *metrics, ngx_str_t *name, mid = ngx_crc32_long(name->data, name->len); - ngx_wasm_shm_lock(metrics->shm); + ngx_wa_shm_lock(metrics->shm); - rc = ngx_wasm_shm_kv_get_locked(metrics->shm, NULL, &mid, &p, &cas); + rc = ngx_wa_shm_kv_get_locked(metrics->shm, NULL, &mid, &p, &cas); if (rc == NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_WASM, metrics->shm->log, 0, "wasm returning existing metric id \"%uD\"", mid); @@ -311,7 +321,7 @@ ngx_wa_metrics_define(ngx_wa_metrics_t *metrics, ngx_str_t *name, val.len = size; val.data = buf; - rc = ngx_wasm_shm_kv_set_locked(metrics->shm, name, &val, 0, &written); + rc = ngx_wa_shm_kv_set_locked(metrics->shm, name, &val, 0, &written); if (rc != NGX_OK) { goto error; } @@ -322,7 +332,7 @@ ngx_wa_metrics_define(ngx_wa_metrics_t *metrics, ngx_str_t *name, error: - ngx_wasm_shm_unlock(metrics->shm); + ngx_wa_shm_unlock(metrics->shm); if (rc == NGX_OK) { ngx_wasm_log_error(NGX_LOG_INFO, metrics->shm->log, 0, @@ -355,11 +365,11 @@ ngx_wa_metrics_increment(ngx_wa_metrics_t *metrics, uint32_t mid, ngx_int_t n) #if 0 if (metrics->shm->eviction != NGX_WASM_EVICTION_NONE) { slot = 0; - ngx_wasm_shm_lock(metrics->shm); + ngx_wa_shm_lock(metrics->shm); } #endif - rc = ngx_wasm_shm_kv_get_locked(metrics->shm, NULL, &mid, &val, &cas); + rc = ngx_wa_shm_kv_get_locked(metrics->shm, NULL, &mid, &val, &cas); if (rc != NGX_OK) { goto error; } @@ -384,7 +394,7 @@ ngx_wa_metrics_increment(ngx_wa_metrics_t *metrics, uint32_t mid, ngx_int_t n) #if 0 if (metrics->shm->eviction != NGX_WASM_EVICTION_NONE) { - ngx_wasm_shm_unlock(metrics->shm); + ngx_wa_shm_unlock(metrics->shm); } #endif @@ -415,11 +425,11 @@ ngx_wa_metrics_record(ngx_wa_metrics_t *metrics, uint32_t mid, ngx_int_t n) #if 0 if (metrics->shm->eviction != NGX_WASM_EVICTION_NONE) { slot = 0; - ngx_wasm_shm_lock(metrics->shm); + ngx_wa_shm_lock(metrics->shm); } #endif - rc = ngx_wasm_shm_kv_get_locked(metrics->shm, NULL, &mid, &val, &cas); + rc = ngx_wa_shm_kv_get_locked(metrics->shm, NULL, &mid, &val, &cas); if (rc != NGX_OK) { goto error; } @@ -448,7 +458,7 @@ ngx_wa_metrics_record(ngx_wa_metrics_t *metrics, uint32_t mid, ngx_int_t n) #if 0 if (metrics->shm->eviction != NGX_WASM_EVICTION_NONE) { - ngx_wasm_shm_unlock(metrics->shm); + ngx_wa_shm_unlock(metrics->shm); } #endif @@ -465,31 +475,37 @@ ngx_wa_metrics_record(ngx_wa_metrics_t *metrics, uint32_t mid, ngx_int_t n) * NGX_DECLINED: not found */ ngx_int_t -ngx_wa_metrics_get(ngx_wa_metrics_t *metrics, uint32_t mid, ngx_uint_t *out) +ngx_wa_metrics_get(ngx_wa_metrics_t *metrics, uint32_t mid, ngx_wa_metric_t *o) { uint32_t cas; ngx_int_t rc; ngx_str_t *n; ngx_wa_metric_t *m; - rc = ngx_wasm_shm_kv_get_locked(metrics->shm, NULL, &mid, &n, &cas); + rc = ngx_wa_shm_kv_get_locked(metrics->shm, NULL, &mid, &n, &cas); if (rc != NGX_OK) { goto done; } m = (ngx_wa_metric_t *) n->data; + o->type = m->type; switch (m->type) { case NGX_WA_METRIC_COUNTER: - *out = get_counter(m, metrics->workers); + o->slots[0].counter = get_counter(m, metrics->workers); break; case NGX_WA_METRIC_GAUGE: - *out = get_gauge(m, metrics->workers); + o->slots[0].gauge.value = get_gauge(m, metrics->workers); + break; + + case NGX_WA_METRIC_HISTOGRAM: + ngx_wa_metrics_histogram_get(metrics, m, metrics->workers, + o->slots[0].histogram); break; default: - ngx_wa_assert(m->type == NGX_WA_METRIC_HISTOGRAM); + ngx_wa_assert(0); rc = NGX_ABORT; break; } diff --git a/src/common/metrics/ngx_wa_metrics.h b/src/common/metrics/ngx_wa_metrics.h index 9a07b9874..ce04f4bc8 100644 --- a/src/common/metrics/ngx_wa_metrics.h +++ b/src/common/metrics/ngx_wa_metrics.h @@ -2,7 +2,21 @@ #define _NGX_WA_METRICS_H_INCLUDED_ -#include +#include + + +#define ngx_wa_metrics_counter(m) m->slots[0].counter +#define ngx_wa_metrics_gauge(m) m->slots[0].gauge.value + + +#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) \ + + sizeof(ngx_wa_metric_val_t) typedef struct ngx_wa_metrics_s ngx_wa_metrics_t; @@ -54,10 +68,10 @@ typedef struct { struct ngx_wa_metrics_s { ngx_uint_t workers; - ngx_shm_zone_t *shm_zone; - ngx_wasm_shm_t *shm; + ngx_wa_shm_t *shm; ngx_wa_metrics_t *old_metrics; ngx_wa_metrics_conf_t config; + ngx_wa_shm_mapping_t *mapping; }; @@ -65,7 +79,7 @@ ngx_str_t *ngx_wa_metric_type_name(ngx_wa_metric_type_e type); ngx_wa_metrics_t *ngx_wa_metrics_alloc(ngx_cycle_t *cycle); char *ngx_wa_metrics_init_conf(ngx_wa_metrics_t *metrics, ngx_conf_t *cf); -ngx_int_t ngx_wa_metrics_init(ngx_wa_metrics_t *metrics, ngx_cycle_t *cycle); +ngx_int_t ngx_wa_metrics_init(ngx_cycle_t *cycle); ngx_int_t ngx_wa_metrics_define(ngx_wa_metrics_t *metrics, ngx_str_t *name, ngx_wa_metric_type_e type, uint32_t *out); @@ -74,7 +88,26 @@ ngx_int_t ngx_wa_metrics_increment(ngx_wa_metrics_t *metrics, ngx_int_t ngx_wa_metrics_record(ngx_wa_metrics_t *metrics, uint32_t metric_id, ngx_int_t val); ngx_int_t ngx_wa_metrics_get(ngx_wa_metrics_t *metrics, uint32_t metric_id, - ngx_uint_t *out); + ngx_wa_metric_t *o); + +ngx_int_t ngx_wa_metrics_histogram_add_locked(ngx_wa_metrics_t *metrics, + ngx_wa_metric_t *m); +ngx_int_t ngx_wa_metrics_histogram_record(ngx_wa_metrics_t *metrics, + ngx_wa_metric_t *m, ngx_uint_t slot, uint32_t mid, ngx_uint_t n); +void ngx_wa_metrics_histogram_get(ngx_wa_metrics_t *metrics, ngx_wa_metric_t *m, + ngx_uint_t slots, ngx_wa_metrics_histogram_t *out); + + +static ngx_inline ngx_wa_metrics_histogram_t * +ngx_wa_metrics_histogram_set_buffer(ngx_wa_metric_t *m, u_char *b, size_t s) +{ + m->slots[0].histogram = (ngx_wa_metrics_histogram_t *) b; + m->slots[0].histogram->n_bins = (s - sizeof(ngx_wa_metrics_histogram_t)) + / sizeof(ngx_wa_metrics_bin_t); + m->slots[0].histogram->bins[0].upper_bound = NGX_MAX_UINT32_VALUE; + + return m->slots[0].histogram; +} #endif /* _NGX_WA_METRICS_H_INCLUDED_ */ diff --git a/src/common/proxy_wasm/ngx_proxy_wasm_host.c b/src/common/proxy_wasm/ngx_proxy_wasm_host.c index 7b306740b..c9958dae9 100644 --- a/src/common/proxy_wasm/ngx_proxy_wasm_host.c +++ b/src/common/proxy_wasm/ngx_proxy_wasm_host.c @@ -8,8 +8,8 @@ #include #include #include -#include -#include +#include +#include #include #ifdef NGX_WASM_HTTP #include @@ -1248,7 +1248,7 @@ ngx_proxy_wasm_hfuncs_get_shared_data(ngx_wavm_instance_t *instance, ngx_str_t *value; uint32_t *value_data, *value_size, *cas; uint32_t wbuf_ptr; - ngx_wasm_shm_kv_key_t resolved; + ngx_wa_shm_kv_key_t resolved; ngx_proxy_wasm_exec_t *pwexec = ngx_proxy_wasm_instance2pwexec(instance); key.len = args[1].of.i32; @@ -1261,7 +1261,7 @@ ngx_proxy_wasm_hfuncs_get_shared_data(ngx_wavm_instance_t *instance, /* resolve key namespace */ - rc = ngx_wasm_shm_kv_resolve_key(&key, &resolved); + rc = ngx_wa_shm_kv_resolve_key(&key, &resolved); if (rc == NGX_ABORT) { return ngx_proxy_wasm_result_trap(pwexec, "attempt to get " "key/value from a queue", rets, @@ -1280,11 +1280,11 @@ ngx_proxy_wasm_hfuncs_get_shared_data(ngx_wavm_instance_t *instance, /* get */ - ngx_wasm_shm_lock(resolved.shm); + ngx_wa_shm_lock(resolved.shm); - rc = ngx_wasm_shm_kv_get_locked(resolved.shm, &key, NULL, &value, cas); + rc = ngx_wa_shm_kv_get_locked(resolved.shm, &key, NULL, &value, cas); - ngx_wasm_shm_unlock(resolved.shm); + ngx_wa_shm_unlock(resolved.shm); if (rc == NGX_DECLINED) { return ngx_proxy_wasm_result_notfound(rets); @@ -1319,7 +1319,7 @@ ngx_proxy_wasm_hfuncs_set_shared_data(ngx_wavm_instance_t *instance, uint32_t cas; ngx_int_t rc, written; ngx_str_t key, value; - ngx_wasm_shm_kv_key_t resolved; + ngx_wa_shm_kv_key_t resolved; ngx_proxy_wasm_exec_t *pwexec = ngx_proxy_wasm_instance2pwexec(instance); key.len = args[1].of.i32; @@ -1334,7 +1334,7 @@ ngx_proxy_wasm_hfuncs_set_shared_data(ngx_wavm_instance_t *instance, /* resolve key namespace */ - rc = ngx_wasm_shm_kv_resolve_key(&key, &resolved); + rc = ngx_wa_shm_kv_resolve_key(&key, &resolved); if (rc == NGX_ABORT) { /* TODO: format with key */ return ngx_proxy_wasm_result_trap(pwexec, "attempt to set " @@ -1354,7 +1354,7 @@ ngx_proxy_wasm_hfuncs_set_shared_data(ngx_wavm_instance_t *instance, /* set */ - ngx_wasm_shm_lock(resolved.shm); + ngx_wa_shm_lock(resolved.shm); /* * If the filter passes a NULL value pointer, treat it as a delete request. @@ -1362,11 +1362,11 @@ ngx_proxy_wasm_hfuncs_set_shared_data(ngx_wavm_instance_t *instance, * - Setting an empty value (ptr != NULL, len == 0) * - Deleting a k/v pair (ptr == NULL, len == 0) */ - rc = ngx_wasm_shm_kv_set_locked(resolved.shm, - &key, value.data ? &value : NULL, - cas, &written); + rc = ngx_wa_shm_kv_set_locked(resolved.shm, + &key, value.data ? &value : NULL, + cas, &written); - ngx_wasm_shm_unlock(resolved.shm); + ngx_wa_shm_unlock(resolved.shm); if (rc == NGX_ERROR) { /* TODO: format with key */ @@ -1396,7 +1396,7 @@ ngx_proxy_wasm_hfuncs_register_shared_queue(ngx_wavm_instance_t *instance, uint32_t *token; ngx_int_t zone_idx; ngx_str_t queue_name; - ngx_wasm_shm_t *shm; + ngx_wa_shm_t *shm; ngx_shm_zone_t *zone; ngx_cycle_t *cycle = (ngx_cycle_t *) ngx_cycle; ngx_proxy_wasm_exec_t *pwexec = ngx_proxy_wasm_instance2pwexec(instance); @@ -1406,18 +1406,18 @@ ngx_proxy_wasm_hfuncs_register_shared_queue(ngx_wavm_instance_t *instance, queue_name.len); token = NGX_WAVM_HOST_LIFT(instance, args[2].of.i32, uint32_t); - zone_idx = ngx_wasm_shm_lookup_index(&queue_name); - if (zone_idx == NGX_WASM_SHM_INDEX_NOTFOUND) { + zone_idx = ngx_wa_shm_lookup_index(&queue_name); + if (zone_idx == NGX_WA_SHM_INDEX_NOTFOUND) { /* TODO: format with queue name */ return ngx_proxy_wasm_result_trap(pwexec, "could not find queue", rets, NGX_WAVM_BAD_USAGE); } - zone = ((ngx_wasm_shm_mapping_t *) - ngx_wasm_core_shms(cycle)->elts)[zone_idx].zone; + zone = ((ngx_wa_shm_mapping_t *) + ngx_wasmx_shms(cycle)->elts)[zone_idx].zone; shm = zone->data; - if (shm->type != NGX_WASM_SHM_TYPE_QUEUE) { + if (shm->type != NGX_WA_SHM_TYPE_QUEUE) { /* TODO: format with shm name */ return ngx_proxy_wasm_result_trap(pwexec, "attempt to use " "a key/value shm store as a queue", @@ -1439,7 +1439,7 @@ ngx_proxy_wasm_hfuncs_enqueue_shared_queue(ngx_wavm_instance_t *instance, ngx_uint_t token; ngx_str_t data; ngx_shm_zone_t *zone; - ngx_wasm_shm_t *shm; + ngx_wa_shm_t *shm; ngx_proxy_wasm_exec_t *pwexec = ngx_proxy_wasm_instance2pwexec(instance); token = args[0].of.i32; @@ -1448,7 +1448,7 @@ ngx_proxy_wasm_hfuncs_enqueue_shared_queue(ngx_wavm_instance_t *instance, /* resolve queue */ - rc = ngx_wasm_shm_queue_resolve(instance->log, token, &zone); + rc = ngx_wa_shm_queue_resolve(instance->log, token, &zone); if (rc == NGX_DECLINED) { /* TODO: format with token */ return ngx_proxy_wasm_result_trap(pwexec, "could not find queue", rets, @@ -1468,9 +1468,9 @@ ngx_proxy_wasm_hfuncs_enqueue_shared_queue(ngx_wavm_instance_t *instance, /* push */ - ngx_wasm_shm_lock(shm); - rc = ngx_wasm_shm_queue_push_locked(shm, &data); - ngx_wasm_shm_unlock(shm); + ngx_wa_shm_lock(shm); + rc = ngx_wa_shm_queue_push_locked(shm, &data); + ngx_wa_shm_unlock(shm); if (rc == NGX_ABORT) { /* TODO: format with queue name */ @@ -1511,7 +1511,7 @@ ngx_proxy_wasm_hfuncs_dequeue_shared_queue(ngx_wavm_instance_t *instance, ngx_uint_t token; ngx_str_t data; ngx_shm_zone_t *zone; - ngx_wasm_shm_t *shm; + ngx_wa_shm_t *shm; uint32_t *wasm_data_ptr; uint32_t *wasm_data_size; ngx_proxy_wasm_exec_t *pwexec = ngx_proxy_wasm_instance2pwexec(instance); @@ -1522,7 +1522,7 @@ ngx_proxy_wasm_hfuncs_dequeue_shared_queue(ngx_wavm_instance_t *instance, /* resolve queue */ - rc = ngx_wasm_shm_queue_resolve(instance->log, token, &zone); + rc = ngx_wa_shm_queue_resolve(instance->log, token, &zone); if (rc == NGX_DECLINED) { /* TODO: format with token */ return ngx_proxy_wasm_result_trap(pwexec, "could not find queue", rets, @@ -1542,10 +1542,9 @@ ngx_proxy_wasm_hfuncs_dequeue_shared_queue(ngx_wavm_instance_t *instance, /* pop */ - ngx_wasm_shm_lock(shm); - rc = ngx_wasm_shm_queue_pop_locked(shm, &data, - shared_queue_alloc, instance); - ngx_wasm_shm_unlock(shm); + ngx_wa_shm_lock(shm); + rc = ngx_wa_shm_queue_pop_locked(shm, &data, shared_queue_alloc, instance); + ngx_wa_shm_unlock(shm); if (rc == NGX_ERROR) { return ngx_proxy_wasm_result_err(rets); @@ -1667,37 +1666,52 @@ ngx_proxy_wasm_hfuncs_get_metric(ngx_wavm_instance_t *instance, ngx_uint_t *ret_value; ngx_cycle_t *cycle = (ngx_cycle_t *) ngx_cycle; 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 trapmsg[NGX_MAX_ERROR_STR]; + ngx_memzero(m_buf, sizeof(m_buf)); + ngx_memzero(h_buf, sizeof(h_buf)); + metric_id = args[0].of.i32; ret_value = NGX_WAVM_HOST_LIFT(instance, args[1].of.i32, ngx_uint_t); - rc = ngx_wa_metrics_get(metrics, metric_id, ret_value); - if (rc != NGX_OK) { - ngx_memzero(trapmsg, NGX_MAX_ERROR_STR); - p = ngx_sprintf(trapmsg, "could not retrieve metric id \"%ui\": ", - metric_id); + m = (ngx_wa_metric_t *) m_buf; + ngx_wa_metrics_histogram_set_buffer(m, h_buf, sizeof(h_buf)); - switch (rc) { - case NGX_DECLINED: - ngx_sprintf(p, "metric not found"); + rc = ngx_wa_metrics_get(metrics, metric_id, m); + if (rc == NGX_OK && m->type != NGX_WA_METRIC_HISTOGRAM) { + switch (m->type) { + case NGX_WA_METRIC_COUNTER: + *ret_value = ngx_wa_metrics_counter(m); break; - case NGX_ABORT: - ngx_sprintf(p, "metric is a histogram"); + case NGX_WA_METRIC_GAUGE: + *ret_value = ngx_wa_metrics_gauge(m); break; default: - ngx_wa_assert(rc == NGX_OK); break; + } - return ngx_proxy_wasm_result_trap(pwexec, (char *) trapmsg, - rets, NGX_WAVM_ERROR); + return ngx_proxy_wasm_result_ok(rets); } - return ngx_proxy_wasm_result_ok(rets); + ngx_memzero(trapmsg, NGX_MAX_ERROR_STR); + p = ngx_sprintf(trapmsg, "could not retrieve metric id \"%ui\"", metric_id); + + if (rc == NGX_DECLINED) { + ngx_sprintf(p, ": metric not found"); + + } else if (m->type == NGX_WA_METRIC_HISTOGRAM) { + ngx_sprintf(p, ": metric is a histogram"); + } + + return ngx_proxy_wasm_result_trap(pwexec, (char *) trapmsg, + rets, NGX_WAVM_ERROR); } diff --git a/src/common/shm/ngx_wasm_shm.c b/src/common/shm/ngx_wa_shm.c similarity index 50% rename from src/common/shm/ngx_wasm_shm.c rename to src/common/shm/ngx_wa_shm.c index ed1204021..af4459660 100644 --- a/src/common/shm/ngx_wasm_shm.c +++ b/src/common/shm/ngx_wa_shm.c @@ -4,14 +4,14 @@ #include "ddebug.h" #include -#include -#include +#include +#include ngx_int_t -ngx_wasm_shm_init_zone(ngx_shm_zone_t *shm_zone, void *data) +ngx_wa_shm_init_zone(ngx_shm_zone_t *shm_zone, void *data) { - ngx_wasm_shm_t *shm = shm_zone->data; + ngx_wa_shm_t *shm = shm_zone->data; dd("zone: %p", shm_zone->shm.addr); @@ -22,13 +22,13 @@ ngx_wasm_shm_init_zone(ngx_shm_zone_t *shm_zone, void *data) ngx_int_t -ngx_wasm_shm_init(ngx_cycle_t *cycle) +ngx_wa_shm_init(ngx_cycle_t *cycle) { - size_t i; - ngx_int_t rc; - ngx_array_t *shms = ngx_wasm_core_shms(cycle); - ngx_wasm_shm_mapping_t *mappings = shms->elts; - ngx_wasm_shm_t *shm; + size_t i; + ngx_int_t rc; + ngx_array_t *shms = ngx_wasmx_shms(cycle); + ngx_wa_shm_mapping_t *mappings = shms->elts; + ngx_wa_shm_t *shm; for (i = 0; i < shms->nelts; i++ ) { shm = mappings[i].zone->data; @@ -39,26 +39,23 @@ ngx_wasm_shm_init(ngx_cycle_t *cycle) &shm->name, mappings[i].zone->shm.addr); switch (shm->type) { - case NGX_WASM_SHM_TYPE_KV: - rc = ngx_wasm_shm_kv_init(shm); - if (rc != NGX_OK) { - return rc; - } - dd("kv init done"); + case NGX_WA_SHM_TYPE_KV: + rc = ngx_wa_shm_kv_init(shm); break; - - case NGX_WASM_SHM_TYPE_QUEUE: - rc = ngx_wasm_shm_queue_init(shm); - if (rc != NGX_OK) { - return rc; - } - dd("queue init done"); + case NGX_WA_SHM_TYPE_QUEUE: + rc = ngx_wa_shm_queue_init(shm); + break; + case NGX_WA_SHM_TYPE_METRICS: + rc = ngx_wa_metrics_init(cycle); break; - default: ngx_wa_assert(0); return NGX_ERROR; } + + if (rc != NGX_OK) { + return rc; + } } return NGX_OK; @@ -66,13 +63,13 @@ ngx_wasm_shm_init(ngx_cycle_t *cycle) ngx_int_t -ngx_wasm_shm_init_process(ngx_cycle_t *cycle) +ngx_wa_shm_init_process(ngx_cycle_t *cycle) { #if (NGX_DEBUG) - size_t i; - ngx_array_t *shms = ngx_wasm_core_shms(cycle); - ngx_wasm_shm_mapping_t *mappings = shms->elts; - ngx_wasm_shm_t *shm; + size_t i; + ngx_array_t *shms = ngx_wasmx_shms(cycle); + ngx_wa_shm_mapping_t *mappings = shms->elts; + ngx_wa_shm_t *shm; for (i = 0; i < shms->nelts; i++ ) { shm = mappings[i].zone->data; @@ -90,14 +87,14 @@ ngx_wasm_shm_init_process(ngx_cycle_t *cycle) ngx_int_t -ngx_wasm_shm_lookup_index(ngx_str_t *name) +ngx_wa_shm_lookup_index(ngx_str_t *name) { - size_t i; - ngx_array_t *shms; - ngx_cycle_t *cycle = (ngx_cycle_t *) ngx_cycle; - ngx_wasm_shm_mapping_t *elements; + size_t i; + ngx_array_t *shms; + ngx_cycle_t *cycle = (ngx_cycle_t *) ngx_cycle; + ngx_wa_shm_mapping_t *elements; - shms = ngx_wasm_core_shms(cycle); + shms = ngx_wasmx_shms(cycle); elements = shms->elts; for (i = 0; i < shms->nelts; i++) { @@ -108,5 +105,5 @@ ngx_wasm_shm_lookup_index(ngx_str_t *name) } } - return NGX_WASM_SHM_INDEX_NOTFOUND; + return NGX_WA_SHM_INDEX_NOTFOUND; } diff --git a/src/common/shm/ngx_wa_shm.h b/src/common/shm/ngx_wa_shm.h new file mode 100644 index 000000000..1e21fe862 --- /dev/null +++ b/src/common/shm/ngx_wa_shm.h @@ -0,0 +1,69 @@ +#ifndef _NGX_WA_SHM_H_INCLUDED_ +#define _NGX_WA_SHM_H_INCLUDED_ + + +#include + + +#define NGX_WA_SHM_MIN_SIZE (3 * ngx_pagesize) +#define NGX_WA_SHM_INDEX_NOTFOUND -1 + + +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 struct { + ngx_str_t name; + ngx_shm_zone_t *zone; +} ngx_wa_shm_mapping_t; + + +ngx_int_t ngx_wa_shm_init(ngx_cycle_t *cycle); +ngx_int_t ngx_wa_shm_init_zone(ngx_shm_zone_t *shm_zone, void *data); +ngx_int_t ngx_wa_shm_init_process(ngx_cycle_t *cycle); +ngx_int_t ngx_wa_shm_lookup_index(ngx_str_t *name); + + +static ngx_inline void +ngx_wa_shm_lock(ngx_wa_shm_t *shm) +{ + ngx_shmtx_lock(&shm->shpool->mutex); +} + + +static ngx_inline void +ngx_wa_shm_unlock(ngx_wa_shm_t *shm) +{ + ngx_shmtx_unlock(&shm->shpool->mutex); +} + + +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_wasm_shm_kv.c b/src/common/shm/ngx_wa_shm_kv.c similarity index 66% rename from src/common/shm/ngx_wasm_shm_kv.c rename to src/common/shm/ngx_wa_shm_kv.c index 834b46212..a2a1d2704 100644 --- a/src/common/shm/ngx_wasm_shm_kv.c +++ b/src/common/shm/ngx_wa_shm_kv.c @@ -4,7 +4,7 @@ #include "ddebug.h" #include -#include +#include /* as defined in nginx/src/core/ngx_slab.c */ @@ -15,26 +15,26 @@ #define NGX_WASM_SLRU_NQUEUES(pool) (NGX_WASM_SLAB_SLOTS(pool) + 1) -ngx_wasm_shm_kv_t * -ngx_wasm_shm_get_kv(ngx_wasm_shm_t *shm) +ngx_wa_shm_kv_t * +ngx_wa_shm_get_kv(ngx_wa_shm_t *shm) { - ngx_wa_assert(shm->type == NGX_WASM_SHM_TYPE_KV || \ - shm->type == NGX_WASM_SHM_TYPE_METRICS); + ngx_wa_assert(shm->type == NGX_WA_SHM_TYPE_KV || \ + shm->type == NGX_WA_SHM_TYPE_METRICS); return shm->data; } ngx_int_t -ngx_wasm_shm_kv_init(ngx_wasm_shm_t *shm) +ngx_wa_shm_kv_init(ngx_wa_shm_t *shm) { - size_t size, i; - ngx_uint_t n; - ngx_wasm_shm_kv_t *kv; + size_t size, i; + ngx_uint_t n; + ngx_wa_shm_kv_t *kv; n = 0; - size = sizeof(ngx_wasm_shm_kv_t); + size = sizeof(ngx_wa_shm_kv_t); - if (shm->eviction == NGX_WASM_SHM_EVICTION_SLRU) { + if (shm->eviction == NGX_WA_SHM_EVICTION_SLRU) { n = NGX_WASM_SLRU_NQUEUES(shm->shpool); size += sizeof(ngx_queue_t) * n; } @@ -48,10 +48,12 @@ ngx_wasm_shm_kv_init(ngx_wasm_shm_t *shm) shm->data = kv; shm->shpool->log_nomem = 0; - if (shm->eviction == NGX_WASM_SHM_EVICTION_LRU) { + kv->nelts = 0; + + if (shm->eviction == NGX_WA_SHM_EVICTION_LRU) { ngx_queue_init(&kv->eviction.lru_queue); - } else if (shm->eviction == NGX_WASM_SHM_EVICTION_SLRU) { + } else if (shm->eviction == NGX_WA_SHM_EVICTION_SLRU) { for (i = 0; i < n; i++) { ngx_queue_init(&kv->eviction.slru_queues[i]); } @@ -65,7 +67,7 @@ ngx_wasm_shm_kv_init(ngx_wasm_shm_t *shm) static ngx_uint_t -slru_index_for_size(ngx_wasm_shm_t *shm, size_t size) +slru_index_for_size(ngx_wa_shm_t *shm, size_t size) { size_t s; ngx_uint_t shift; @@ -83,7 +85,7 @@ slru_index_for_size(ngx_wasm_shm_t *shm, size_t size) /** * The above condition will always be true as long as - * sizeof(ngx_wasm_shm_kv_node_t) > shm->shpool->min_size. We do not assert + * sizeof(ngx_wa_shm_kv_node_t) > shm->shpool->min_size. We do not assert * it unconditionally because min_size depends on the Nginx page size * configuration. The fallback case is to use the first queue for small * items, strictly following the Nginx slot selection algorithm. @@ -93,17 +95,17 @@ slru_index_for_size(ngx_wasm_shm_t *shm, size_t size) static ngx_queue_t* -queue_for_node(ngx_wasm_shm_t *shm, ngx_wasm_shm_kv_node_t *n) +queue_for_node(ngx_wa_shm_t *shm, ngx_wa_shm_kv_node_t *n) { - size_t size; - ngx_wasm_shm_kv_t *kv = ngx_wasm_shm_get_kv(shm); + size_t size; + ngx_wa_shm_kv_t *kv = ngx_wa_shm_get_kv(shm); - if (shm->eviction == NGX_WASM_SHM_EVICTION_LRU) { + if (shm->eviction == NGX_WA_SHM_EVICTION_LRU) { return &kv->eviction.lru_queue; } - if (shm->eviction == NGX_WASM_SHM_EVICTION_SLRU) { - size = sizeof(ngx_wasm_shm_kv_node_t) + if (shm->eviction == NGX_WA_SHM_EVICTION_SLRU) { + size = sizeof(ngx_wa_shm_kv_node_t) + n->key.str.len + n->value.len; @@ -116,18 +118,18 @@ queue_for_node(ngx_wasm_shm_t *shm, ngx_wasm_shm_kv_node_t *n) } -static ngx_wasm_shm_kv_node_t * -ngx_wasm_shm_rbtree_lookup(ngx_rbtree_t *rbtree, uint32_t key_hash) +static ngx_wa_shm_kv_node_t * +ngx_wa_shm_rbtree_lookup(ngx_rbtree_t *rbtree, uint32_t key_hash) { - ngx_wasm_shm_kv_node_t *n; - ngx_rbtree_node_t *node, *sentinel; + ngx_rbtree_node_t *node, *sentinel; + ngx_wa_shm_kv_node_t *n; node = rbtree->root; sentinel = rbtree->sentinel; while (node != sentinel) { - n = (ngx_wasm_shm_kv_node_t *) node; + n = (ngx_wa_shm_kv_node_t *) node; if (key_hash != node->key) { node = (key_hash < node->key) ? node->left : node->right; @@ -142,26 +144,26 @@ ngx_wasm_shm_rbtree_lookup(ngx_rbtree_t *rbtree, uint32_t key_hash) ngx_int_t -ngx_wasm_shm_kv_get_locked(ngx_wasm_shm_t *shm, ngx_str_t *key, +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_wasm_shm_kv_t *kv = ngx_wasm_shm_get_kv(shm); - ngx_wasm_shm_kv_node_t *n; + ngx_wa_shm_kv_t *kv = ngx_wa_shm_get_kv(shm); + ngx_wa_shm_kv_node_t *n; if (key_hash) { - n = ngx_wasm_shm_rbtree_lookup(&kv->rbtree, *key_hash); + n = ngx_wa_shm_rbtree_lookup(&kv->rbtree, *key_hash); } else { - n = ngx_wasm_shm_rbtree_lookup(&kv->rbtree, - ngx_crc32_long(key->data, key->len)); + n = ngx_wa_shm_rbtree_lookup(&kv->rbtree, + ngx_crc32_long(key->data, key->len)); } if (n == NULL) { return NGX_DECLINED; } - if (shm->eviction == NGX_WASM_SHM_EVICTION_LRU - || shm->eviction == NGX_WASM_SHM_EVICTION_SLRU) + if (shm->eviction == NGX_WA_SHM_EVICTION_LRU + || shm->eviction == NGX_WA_SHM_EVICTION_SLRU) { ngx_queue_remove(&n->queue); ngx_queue_insert_head(queue_for_node(shm, n), &n->queue); @@ -180,12 +182,12 @@ ngx_wasm_shm_kv_get_locked(ngx_wasm_shm_t *shm, ngx_str_t *key, static ngx_int_t -queue_expire(ngx_wasm_shm_t *shm, ngx_queue_t *queue, ngx_queue_t *q) +queue_expire(ngx_wa_shm_t *shm, ngx_queue_t *queue, ngx_queue_t *q) { - ngx_wasm_shm_kv_node_t *node; - ngx_wasm_shm_kv_t *kv = ngx_wasm_shm_get_kv(shm); + ngx_wa_shm_kv_node_t *node; + ngx_wa_shm_kv_t *kv = ngx_wa_shm_get_kv(shm); - node = ngx_queue_data(q, ngx_wasm_shm_kv_node_t, queue); + node = ngx_queue_data(q, ngx_wa_shm_kv_node_t, queue); ngx_queue_remove(q); @@ -193,17 +195,19 @@ queue_expire(ngx_wasm_shm_t *shm, ngx_queue_t *queue, ngx_queue_t *q) ngx_slab_free_locked(shm->shpool, node); + kv->nelts--; + return NGX_OK; } static ngx_int_t -slru_expire(ngx_wasm_shm_t *shm, size_t size) +slru_expire(ngx_wa_shm_t *shm, size_t size) { - ngx_int_t i; - ngx_uint_t n, start; - ngx_queue_t *q, *queue; - ngx_wasm_shm_kv_t *kv = ngx_wasm_shm_get_kv(shm); + ngx_int_t i; + ngx_uint_t n, start; + ngx_queue_t *q, *queue; + ngx_wa_shm_kv_t *kv = ngx_wa_shm_get_kv(shm); n = NGX_WASM_SLRU_NQUEUES(shm->shpool); start = slru_index_for_size(shm, size); @@ -235,10 +239,10 @@ slru_expire(ngx_wasm_shm_t *shm, size_t size) static ngx_int_t -lru_expire(ngx_wasm_shm_t *shm) +lru_expire(ngx_wa_shm_t *shm) { - ngx_wasm_shm_kv_t *kv = ngx_wasm_shm_get_kv(shm); - ngx_queue_t *q, *lru_queue = &kv->eviction.lru_queue; + ngx_wa_shm_kv_t *kv = ngx_wa_shm_get_kv(shm); + ngx_queue_t *q, *lru_queue = &kv->eviction.lru_queue; q = ngx_queue_last(lru_queue); if (q == ngx_queue_sentinel(lru_queue)) { @@ -250,10 +254,10 @@ lru_expire(ngx_wasm_shm_t *shm) static ngx_inline void -node_queue_remove(ngx_wasm_shm_t *shm, ngx_wasm_shm_kv_node_t *n) +node_queue_remove(ngx_wa_shm_t *shm, ngx_wa_shm_kv_node_t *n) { - if (shm->eviction == NGX_WASM_SHM_EVICTION_LRU - || shm->eviction == NGX_WASM_SHM_EVICTION_SLRU) + if (shm->eviction == NGX_WA_SHM_EVICTION_LRU + || shm->eviction == NGX_WA_SHM_EVICTION_SLRU) { ngx_queue_remove(&n->queue); } @@ -261,16 +265,16 @@ node_queue_remove(ngx_wasm_shm_t *shm, ngx_wasm_shm_kv_node_t *n) ngx_int_t -ngx_wasm_shm_kv_set_locked(ngx_wasm_shm_t *shm, ngx_str_t *key, +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) { - size_t size; - uint32_t key_hash = ngx_crc32_long(key->data, key->len); - ngx_wasm_shm_kv_t *kv = ngx_wasm_shm_get_kv(shm); - ngx_wasm_shm_kv_node_t *n, *old; + size_t size; + uint32_t key_hash = ngx_crc32_long(key->data, key->len); + ngx_wa_shm_kv_t *kv = ngx_wa_shm_get_kv(shm); + ngx_wa_shm_kv_node_t *n, *old; old = NULL; - n = ngx_wasm_shm_rbtree_lookup(&kv->rbtree, key_hash); + n = ngx_wa_shm_rbtree_lookup(&kv->rbtree, key_hash); if (cas != (n == NULL ? 0 : n->cas)) { *written = 0; @@ -287,6 +291,7 @@ ngx_wasm_shm_kv_set_locked(ngx_wasm_shm_t *shm, ngx_str_t *key, ngx_rbtree_delete(&kv->rbtree, &n->key.node); ngx_slab_free_locked(shm->shpool, n); *written = 1; + kv->nelts--; } else { *written = 0; @@ -303,7 +308,7 @@ ngx_wasm_shm_kv_set_locked(ngx_wasm_shm_t *shm, ngx_str_t *key, } if (n == NULL) { - size = sizeof(ngx_wasm_shm_kv_node_t) + key->len + value->len; + size = sizeof(ngx_wa_shm_kv_node_t) + key->len + value->len; for ( ;; ) { n = ngx_slab_calloc_locked(shm->shpool, size); @@ -311,9 +316,9 @@ ngx_wasm_shm_kv_set_locked(ngx_wasm_shm_t *shm, ngx_str_t *key, break; } - if ((shm->eviction == NGX_WASM_SHM_EVICTION_LRU + if ((shm->eviction == NGX_WA_SHM_EVICTION_LRU && lru_expire(shm) == NGX_OK) || - (shm->eviction == NGX_WASM_SHM_EVICTION_SLRU + (shm->eviction == NGX_WA_SHM_EVICTION_SLRU && slru_expire(shm, size) == NGX_OK)) { ngx_log_debug1(NGX_LOG_DEBUG_WASM, shm->log, 0, @@ -331,7 +336,7 @@ ngx_wasm_shm_kv_set_locked(ngx_wasm_shm_t *shm, ngx_str_t *key, return NGX_ERROR; } - n->key.str.data = (u_char *) n + sizeof(ngx_wasm_shm_kv_node_t); + n->key.str.data = (u_char *) n + sizeof(ngx_wa_shm_kv_node_t); n->key.str.len = key->len; n->key.node.key = ngx_crc32_long(key->data, key->len); n->value.data = n->key.str.data + key->len; @@ -343,6 +348,9 @@ ngx_wasm_shm_kv_set_locked(ngx_wasm_shm_t *shm, ngx_str_t *key, n->cas = old->cas; ngx_rbtree_delete(&kv->rbtree, &old->key.node); ngx_slab_free_locked(shm->shpool, old); + + } else { + kv->nelts++; } ngx_memcpy(n->key.str.data, key->data, key->len); @@ -350,8 +358,8 @@ ngx_wasm_shm_kv_set_locked(ngx_wasm_shm_t *shm, ngx_str_t *key, ngx_rbtree_insert(&kv->rbtree, &n->key.node); - if (shm->eviction == NGX_WASM_SHM_EVICTION_LRU - || shm->eviction == NGX_WASM_SHM_EVICTION_SLRU) + if (shm->eviction == NGX_WA_SHM_EVICTION_LRU + || shm->eviction == NGX_WA_SHM_EVICTION_SLRU) { ngx_queue_insert_head(queue_for_node(shm, n), &n->queue); } @@ -359,8 +367,8 @@ ngx_wasm_shm_kv_set_locked(ngx_wasm_shm_t *shm, ngx_str_t *key, } else { n->value.len = value->len; - if (shm->eviction == NGX_WASM_SHM_EVICTION_LRU - || shm->eviction == NGX_WASM_SHM_EVICTION_SLRU) + if (shm->eviction == NGX_WA_SHM_EVICTION_LRU + || shm->eviction == NGX_WA_SHM_EVICTION_SLRU) { ngx_queue_remove(&n->queue); ngx_queue_insert_head(queue_for_node(shm, n), &n->queue); @@ -380,18 +388,18 @@ ngx_wasm_shm_kv_set_locked(ngx_wasm_shm_t *shm, ngx_str_t *key, ngx_int_t -ngx_wasm_shm_kv_resolve_key(ngx_str_t *key, ngx_wasm_shm_kv_key_t *out) +ngx_wa_shm_kv_resolve_key(ngx_str_t *key, ngx_wa_shm_kv_key_t *out) { size_t i; - ngx_int_t zone_idx = NGX_WASM_SHM_INDEX_NOTFOUND; + ngx_int_t zone_idx = NGX_WA_SHM_INDEX_NOTFOUND; ngx_shm_zone_t *zone; ngx_array_t *zone_array; ngx_cycle_t *cycle = (ngx_cycle_t *) ngx_cycle; static const ngx_str_t default_namespace = ngx_string("*"); - ngx_memzero(out, sizeof(ngx_wasm_shm_kv_key_t)); + ngx_memzero(out, sizeof(ngx_wa_shm_kv_key_t)); - zone_array = ngx_wasm_core_shms(cycle); + zone_array = ngx_wasmx_shms(cycle); if (zone_array == NULL) { return NGX_DECLINED; } @@ -407,10 +415,10 @@ ngx_wasm_shm_kv_resolve_key(ngx_str_t *key, ngx_wasm_shm_kv_key_t *out) } if (out->namespace.len) { - zone_idx = ngx_wasm_shm_lookup_index(&out->namespace); + zone_idx = ngx_wa_shm_lookup_index(&out->namespace); } - if (zone_idx == NGX_WASM_SHM_INDEX_NOTFOUND) { + if (zone_idx == NGX_WA_SHM_INDEX_NOTFOUND) { /** * If the key does not contain a namespace prefix, or the prefix does not * match any defined namespace, attempt to use the default namespace @@ -419,17 +427,17 @@ ngx_wasm_shm_kv_resolve_key(ngx_str_t *key, ngx_wasm_shm_kv_key_t *out) out->namespace.len = default_namespace.len; out->key = *key; - zone_idx = ngx_wasm_shm_lookup_index(&out->namespace); - if (zone_idx == NGX_WASM_SHM_INDEX_NOTFOUND) { + zone_idx = ngx_wa_shm_lookup_index(&out->namespace); + if (zone_idx == NGX_WA_SHM_INDEX_NOTFOUND) { return NGX_DECLINED; } } - zone = ((ngx_wasm_shm_mapping_t *) zone_array->elts)[zone_idx].zone; + zone = ((ngx_wa_shm_mapping_t *) zone_array->elts)[zone_idx].zone; out->zone = zone; out->shm = zone->data; - if (out->shm->type != NGX_WASM_SHM_TYPE_KV) { + if (out->shm->type != NGX_WA_SHM_TYPE_KV) { return NGX_ABORT; } diff --git a/src/common/shm/ngx_wa_shm_kv.h b/src/common/shm/ngx_wa_shm_kv.h new file mode 100644 index 000000000..d67ed35d9 --- /dev/null +++ b/src/common/shm/ngx_wa_shm_kv.h @@ -0,0 +1,45 @@ +#ifndef _NGX_WA_SHM_KV_H_INCLUDED_ +#define _NGX_WA_SHM_KV_H_INCLUDED_ + + +#include + + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_uint_t nelts; + union { + ngx_queue_t lru_queue; + ngx_queue_t slru_queues[0]; + } eviction; +} ngx_wa_shm_kv_t; + + +typedef struct { + ngx_str_t namespace; + ngx_str_t key; + ngx_shm_zone_t *zone; + ngx_wa_shm_t *shm; +} ngx_wa_shm_kv_key_t; + + +typedef struct { + ngx_str_node_t key; + ngx_str_t value; + uint32_t cas; + ngx_queue_t queue; +} ngx_wa_shm_kv_node_t; + + +ngx_wa_shm_kv_t * ngx_wa_shm_get_kv(ngx_wa_shm_t *shm); + +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_int_t ngx_wa_shm_kv_resolve_key(ngx_str_t *key, ngx_wa_shm_kv_key_t *out); + + +#endif /* _NGX_WA_SHM_KV_H_INCLUDED_ */ diff --git a/src/common/shm/ngx_wasm_shm_queue.c b/src/common/shm/ngx_wa_shm_queue.c similarity index 76% rename from src/common/shm/ngx_wasm_shm_queue.c rename to src/common/shm/ngx_wa_shm_queue.c index 55093602e..4ad51a502 100644 --- a/src/common/shm/ngx_wasm_shm_queue.c +++ b/src/common/shm/ngx_wa_shm_queue.c @@ -4,7 +4,7 @@ #include "ddebug.h" #include -#include +#include typedef struct { @@ -13,26 +13,26 @@ typedef struct { ngx_uint_t push_ptr; ngx_uint_t pop_ptr; ngx_uint_t rising_occupancy; -} ngx_wasm_shm_queue_t; +} ngx_wa_shm_queue_t; -static ngx_inline ngx_wasm_shm_queue_t * -ngx_wasm_shm_get_queue(ngx_wasm_shm_t *shm) +static ngx_inline ngx_wa_shm_queue_t * +ngx_wa_shm_get_queue(ngx_wa_shm_t *shm) { - ngx_wa_assert(shm->type == NGX_WASM_SHM_TYPE_QUEUE); + ngx_wa_assert(shm->type == NGX_WA_SHM_TYPE_QUEUE); return shm->data; } static ngx_inline size_t -queue_capacity(ngx_wasm_shm_queue_t *queue) +queue_capacity(ngx_wa_shm_queue_t *queue) { return queue->buffer_end - queue->buffer; } static ngx_inline size_t -queue_occupancy(ngx_wasm_shm_queue_t *queue) +queue_occupancy(ngx_wa_shm_queue_t *queue) { if (queue->push_ptr > queue->pop_ptr) { return queue->push_ptr - queue->pop_ptr; @@ -47,7 +47,7 @@ queue_occupancy(ngx_wasm_shm_queue_t *queue) static ngx_inline void -inc_ptr(ngx_wasm_shm_queue_t *queue, ngx_uint_t *ptr, ngx_uint_t n) +inc_ptr(ngx_wa_shm_queue_t *queue, ngx_uint_t *ptr, ngx_uint_t n) { ngx_uint_t new_ptr = *ptr + n; ngx_uint_t cap = queue_capacity(queue); @@ -62,7 +62,7 @@ inc_ptr(ngx_wasm_shm_queue_t *queue, ngx_uint_t *ptr, ngx_uint_t n) static ngx_inline void -circular_write(ngx_log_t *log, ngx_wasm_shm_queue_t *queue, +circular_write(ngx_log_t *log, ngx_wa_shm_queue_t *queue, ngx_uint_t ptr, void *data, ngx_uint_t data_size) { ngx_uint_t cap = queue_capacity(queue); @@ -89,7 +89,7 @@ circular_write(ngx_log_t *log, ngx_wasm_shm_queue_t *queue, static ngx_inline void -circular_read(ngx_log_t *log, ngx_wasm_shm_queue_t *queue, +circular_read(ngx_log_t *log, ngx_wa_shm_queue_t *queue, ngx_uint_t ptr, void *data_out, ngx_uint_t data_size) { ngx_uint_t cap = queue_capacity(queue); @@ -114,7 +114,7 @@ circular_read(ngx_log_t *log, ngx_wasm_shm_queue_t *queue, static ngx_inline void -check_queue_invariance(ngx_wasm_shm_queue_t *queue) +check_queue_invariance(ngx_wa_shm_queue_t *queue) { #if (NGX_DEBUG) ngx_uint_t cap = queue_capacity(queue); @@ -126,13 +126,13 @@ check_queue_invariance(ngx_wasm_shm_queue_t *queue) ngx_int_t -ngx_wasm_shm_queue_init(ngx_wasm_shm_t *shm) +ngx_wa_shm_queue_init(ngx_wa_shm_t *shm) { - ngx_uint_t buffer_size; - ngx_uint_t reserved_size = ngx_pagesize; - ngx_wasm_shm_queue_t *queue; + ngx_uint_t buffer_size; + ngx_uint_t reserved_size = ngx_pagesize; + ngx_wa_shm_queue_t *queue; - queue = ngx_slab_calloc(shm->shpool, sizeof(ngx_wasm_shm_queue_t)); + queue = ngx_slab_calloc(shm->shpool, sizeof(ngx_wa_shm_queue_t)); if (queue == NULL) { dd("failed allocating queue structure"); return NGX_ERROR; @@ -162,11 +162,11 @@ ngx_wasm_shm_queue_init(ngx_wasm_shm_t *shm) ngx_int_t -ngx_wasm_shm_queue_push_locked(ngx_wasm_shm_t *shm, ngx_str_t *data) +ngx_wa_shm_queue_push_locked(ngx_wa_shm_t *shm, ngx_str_t *data) { - uint32_t len = (uint32_t) data->len; - ngx_uint_t entry_size = sizeof(uint32_t) + data->len; - ngx_wasm_shm_queue_t *queue = ngx_wasm_shm_get_queue(shm); + uint32_t len = (uint32_t) data->len; + ngx_uint_t entry_size = sizeof(uint32_t) + data->len; + ngx_wa_shm_queue_t *queue = ngx_wa_shm_get_queue(shm); /* queue full? */ @@ -191,12 +191,12 @@ ngx_wasm_shm_queue_push_locked(ngx_wasm_shm_t *shm, ngx_str_t *data) ngx_int_t -ngx_wasm_shm_queue_pop_locked(ngx_wasm_shm_t *shm, ngx_str_t *data_out, - ngx_wasm_shm_queue_alloc_pt alloc, void *alloc_ctx) +ngx_wa_shm_queue_pop_locked(ngx_wa_shm_t *shm, ngx_str_t *data_out, + ngx_wa_shm_queue_alloc_pt alloc, void *alloc_ctx) { - uint32_t len; - void *buf = NULL; - ngx_wasm_shm_queue_t *queue = ngx_wasm_shm_get_queue(shm); + uint32_t len; + void *buf = NULL; + ngx_wa_shm_queue_t *queue = ngx_wa_shm_get_queue(shm); /* queue empty? */ @@ -234,14 +234,14 @@ ngx_wasm_shm_queue_pop_locked(ngx_wasm_shm_t *shm, ngx_str_t *data_out, ngx_int_t -ngx_wasm_shm_queue_resolve(ngx_log_t *log, uint32_t token, ngx_shm_zone_t **out) +ngx_wa_shm_queue_resolve(ngx_log_t *log, uint32_t token, ngx_shm_zone_t **out) { - ngx_wasm_shm_t *shm; + ngx_wa_shm_t *shm; ngx_shm_zone_t *zone; ngx_array_t *zone_array; ngx_cycle_t *cycle = (ngx_cycle_t *) ngx_cycle; - zone_array = ngx_wasm_core_shms(cycle); + zone_array = ngx_wasmx_shms(cycle); if (zone_array == NULL) { return NGX_DECLINED; } @@ -250,11 +250,11 @@ ngx_wasm_shm_queue_resolve(ngx_log_t *log, uint32_t token, ngx_shm_zone_t **out) return NGX_DECLINED; } - zone = ((ngx_wasm_shm_mapping_t *) + zone = ((ngx_wa_shm_mapping_t *) zone_array->elts)[token].zone; shm = zone->data; - if (shm->type != NGX_WASM_SHM_TYPE_QUEUE) { + if (shm->type != NGX_WA_SHM_TYPE_QUEUE) { return NGX_ABORT; } diff --git a/src/common/shm/ngx_wa_shm_queue.h b/src/common/shm/ngx_wa_shm_queue.h new file mode 100644 index 000000000..74c855ebf --- /dev/null +++ b/src/common/shm/ngx_wa_shm_queue.h @@ -0,0 +1,19 @@ +#ifndef _NGX_WA_SHM_QUEUE_H_INCLUDED_ +#define _NGX_WA_SHM_QUEUE_H_INCLUDED_ + + +#include + + +typedef void *(*ngx_wa_shm_queue_alloc_pt)(size_t size, void *alloc_ctx); + + +ngx_int_t ngx_wa_shm_queue_init(ngx_wa_shm_t *shm); +ngx_int_t ngx_wa_shm_queue_push_locked(ngx_wa_shm_t *shm, ngx_str_t *data); +ngx_int_t ngx_wa_shm_queue_pop_locked(ngx_wa_shm_t *shm, + ngx_str_t *data_out, ngx_wa_shm_queue_alloc_pt alloc, void *alloc_ctx); +ngx_int_t ngx_wa_shm_queue_resolve(ngx_log_t *log, uint32_t token, + ngx_shm_zone_t **out); + + +#endif /* _NGX_WA_SHM_QUEUE_H_INCLUDED_ */ diff --git a/src/common/shm/ngx_wasm_shm.h b/src/common/shm/ngx_wasm_shm.h deleted file mode 100644 index aa2b0bc87..000000000 --- a/src/common/shm/ngx_wasm_shm.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef _NGX_WASM_SHM_H_INCLUDED_ -#define _NGX_WASM_SHM_H_INCLUDED_ - - -#include - - -#define NGX_WASM_SHM_MIN_SIZE (3 * ngx_pagesize) -#define NGX_WASM_SHM_INDEX_NOTFOUND -1 - - -typedef enum { - NGX_WASM_SHM_TYPE_KV, - NGX_WASM_SHM_TYPE_QUEUE, - NGX_WASM_SHM_TYPE_METRICS, -} ngx_wasm_shm_type_e; - - -typedef enum { - NGX_WASM_SHM_EVICTION_LRU, - NGX_WASM_SHM_EVICTION_SLRU, - NGX_WASM_SHM_EVICTION_NONE, -} ngx_wasm_shm_eviction_e; - - -typedef struct { - ngx_wasm_shm_type_e type; - ngx_wasm_shm_eviction_e eviction; - ngx_str_t name; - ngx_log_t *log; - ngx_slab_pool_t *shpool; - void *data; -} ngx_wasm_shm_t; - - -typedef struct { - ngx_str_t name; - ngx_shm_zone_t *zone; -} ngx_wasm_shm_mapping_t; - - -ngx_array_t *ngx_wasm_core_shms(ngx_cycle_t *cycle); - -ngx_int_t ngx_wasm_shm_init(ngx_cycle_t *cycle); -ngx_int_t ngx_wasm_shm_init_zone(ngx_shm_zone_t *shm_zone, void *data); -ngx_int_t ngx_wasm_shm_init_process(ngx_cycle_t *cycle); -ngx_int_t ngx_wasm_shm_lookup_index(ngx_str_t *name); - - -static ngx_inline void -ngx_wasm_shm_lock(ngx_wasm_shm_t *shm) -{ - ngx_shmtx_lock(&shm->shpool->mutex); -} - - -static ngx_inline void -ngx_wasm_shm_unlock(ngx_wasm_shm_t *shm) -{ - ngx_shmtx_unlock(&shm->shpool->mutex); -} - - -#endif /* _NGX_WASM_SHM_H_INCLUDED_ */ diff --git a/src/common/shm/ngx_wasm_shm_kv.h b/src/common/shm/ngx_wasm_shm_kv.h deleted file mode 100644 index d711b6b57..000000000 --- a/src/common/shm/ngx_wasm_shm_kv.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef _NGX_WASM_SHM_KV_H_INCLUDED_ -#define _NGX_WASM_SHM_KV_H_INCLUDED_ - - -#include - - -typedef struct { - ngx_rbtree_t rbtree; - ngx_rbtree_node_t sentinel; - union { - ngx_queue_t lru_queue; - ngx_queue_t slru_queues[0]; - } eviction; -} ngx_wasm_shm_kv_t; - - -typedef struct { - ngx_str_t namespace; - ngx_str_t key; - ngx_shm_zone_t *zone; - ngx_wasm_shm_t *shm; -} ngx_wasm_shm_kv_key_t; - - -typedef struct { - ngx_str_node_t key; - ngx_str_t value; - uint32_t cas; - ngx_queue_t queue; -} ngx_wasm_shm_kv_node_t; - - -ngx_wasm_shm_kv_t * ngx_wasm_shm_get_kv(ngx_wasm_shm_t *shm); - -ngx_int_t ngx_wasm_shm_kv_init(ngx_wasm_shm_t *shm); -ngx_int_t ngx_wasm_shm_kv_get_locked(ngx_wasm_shm_t *shm, - ngx_str_t *key, uint32_t *key_hash, ngx_str_t **value_out, uint32_t *cas); -ngx_int_t ngx_wasm_shm_kv_set_locked(ngx_wasm_shm_t *shm, - ngx_str_t *key, ngx_str_t *value, uint32_t cas, ngx_int_t *written); -ngx_int_t ngx_wasm_shm_kv_resolve_key(ngx_str_t *key, - ngx_wasm_shm_kv_key_t *out); - - -#endif /* _NGX_WASM_SHM_KV_H_INCLUDED_ */ diff --git a/src/common/shm/ngx_wasm_shm_queue.h b/src/common/shm/ngx_wasm_shm_queue.h deleted file mode 100644 index a1d3238d7..000000000 --- a/src/common/shm/ngx_wasm_shm_queue.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _NGX_WASM_SHM_QUEUE_H_INCLUDED_ -#define _NGX_WASM_SHM_QUEUE_H_INCLUDED_ - - -#include - - -typedef void *(*ngx_wasm_shm_queue_alloc_pt)(size_t size, void *alloc_ctx); - - -ngx_int_t ngx_wasm_shm_queue_init(ngx_wasm_shm_t *shm); -ngx_int_t ngx_wasm_shm_queue_push_locked(ngx_wasm_shm_t *shm, ngx_str_t *data); -ngx_int_t ngx_wasm_shm_queue_pop_locked(ngx_wasm_shm_t *shm, - ngx_str_t *data_out, ngx_wasm_shm_queue_alloc_pt alloc, void *alloc_ctx); -ngx_int_t ngx_wasm_shm_queue_resolve(ngx_log_t *log, uint32_t token, - ngx_shm_zone_t **out); - - -#endif /* _NGX_WASM_SHM_QUEUE_H_INCLUDED_ */ diff --git a/src/ngx_wasmx.c b/src/ngx_wasmx.c index ba5acc860..26a4d087d 100644 --- a/src/ngx_wasmx.c +++ b/src/ngx_wasmx.c @@ -14,6 +14,7 @@ static char *ngx_wasm_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_ipc_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); #endif static ngx_int_t ngx_wasmx_init(ngx_cycle_t *cycle); +static ngx_int_t ngx_wasmx_init_process(ngx_cycle_t *cycle); ngx_uint_t ngx_wasm_max_module = 0; @@ -56,7 +57,7 @@ ngx_module_t ngx_wasmx_module = { NGX_CORE_MODULE, /* module type */ NULL, /* init master */ ngx_wasmx_init, /* init module */ - NULL, /* init process */ + ngx_wasmx_init_process, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ @@ -113,6 +114,13 @@ ngx_wasmx_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf, } #endif + if (ngx_array_init(&wacf->shms, cf->pool, + 1, sizeof(ngx_wa_shm_mapping_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + wacf->metrics = ngx_wa_metrics_alloc(cf->cycle); if (wacf->metrics == NULL) { return NGX_CONF_ERROR; @@ -258,9 +266,8 @@ ngx_wasmx_init(ngx_cycle_t *cycle) return NGX_OK; } - rc = ngx_wa_metrics_init(wacf->metrics, cycle); - if (rc != NGX_OK) { - return rc; + if (ngx_wa_shm_init(cycle) != NGX_OK) { + return NGX_ERROR; } /* NGX_WASM_MODULES + NGX_IPC_MODULES init */ @@ -302,6 +309,38 @@ ngx_wasmx_init(ngx_cycle_t *cycle) } +static ngx_int_t +ngx_wasmx_init_process(ngx_cycle_t *cycle) +{ + ngx_wa_conf_t *wacf; + + wacf = ngx_wa_cycle_get_conf(cycle); + if (wacf == NULL) { + return NGX_OK; + } + + if (ngx_wa_shm_init_process(cycle) != NGX_OK) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_inline ngx_array_t * +ngx_wasmx_shms(ngx_cycle_t *cycle) +{ + ngx_wa_conf_t *wacf; + + wacf = ngx_wa_cycle_get_conf(cycle); + if (wacf == NULL) { + return NULL; + } + + return &wacf->shms; +} + + ngx_inline ngx_wa_metrics_t * ngx_wasmx_metrics(ngx_cycle_t *cycle) { diff --git a/src/ngx_wasmx.h b/src/ngx_wasmx.h index 911d78536..2abe681b0 100644 --- a/src/ngx_wasmx.h +++ b/src/ngx_wasmx.h @@ -3,6 +3,7 @@ #include +#include #include @@ -38,10 +39,12 @@ typedef struct { #ifdef NGX_WA_IPC void **ipc_confs; #endif + ngx_array_t shms; /* ngx_wa_shm_mapping_t */ ngx_wa_metrics_t *metrics; } ngx_wa_conf_t; +ngx_array_t *ngx_wasmx_shms(ngx_cycle_t *cycle); ngx_wa_metrics_t *ngx_wasmx_metrics(ngx_cycle_t *cycle); diff --git a/src/wasm/ngx_wasm.h b/src/wasm/ngx_wasm.h index bc8d66b42..01554203a 100644 --- a/src/wasm/ngx_wasm.h +++ b/src/wasm/ngx_wasm.h @@ -4,7 +4,6 @@ #include #include -#include #if (NGX_SSL) #include #endif @@ -83,7 +82,6 @@ typedef struct { typedef struct { ngx_wavm_t *vm; ngx_wavm_conf_t vm_conf; - ngx_array_t shms; /* element: ngx_wasm_shm_mapping_t */ #if (NGX_SSL) ngx_wasm_ssl_conf_t ssl_conf; #endif diff --git a/src/wasm/ngx_wasm_core_module.c b/src/wasm/ngx_wasm_core_module.c index 59bac7def..1b6dca923 100644 --- a/src/wasm/ngx_wasm_core_module.c +++ b/src/wasm/ngx_wasm_core_module.c @@ -4,7 +4,6 @@ #include "ddebug.h" #include -#include #include @@ -257,18 +256,6 @@ ngx_wasm_main_vm(ngx_cycle_t *cycle) } -ngx_inline ngx_array_t * -ngx_wasm_core_shms(ngx_cycle_t *cycle) -{ - ngx_wasm_core_conf_t *wcf; - - wcf = ngx_wasm_core_cycle_get_conf(cycle); - ngx_wa_assert(wcf); - - return &wcf->shms; -} - - static void ngx_wasm_core_cleanup_pool(void *data) { @@ -354,17 +341,6 @@ ngx_wasm_core_create_conf(ngx_conf_t *cf) cln->handler = ngx_wasm_core_cleanup_pool; cln->data = cycle; - if (ngx_array_init(&wcf->shms, cycle->pool, - 1, sizeof(ngx_wasm_shm_mapping_t)) - != NGX_OK) - { - /* - * future ngx_array_push calls will fail allocating memory and cause - * silent pool corruption - */ - return NULL; - } - wcf->vm_conf.vm_name = wcf->vm->name; wcf->vm_conf.runtime_name = &runtime_name; wcf->vm_conf.backtraces = NGX_CONF_UNSET; @@ -482,10 +458,6 @@ ngx_wasm_core_init(ngx_cycle_t *cycle) return NGX_ERROR; } - if (ngx_wasm_shm_init(cycle) != NGX_OK) { - return NGX_ERROR; - } - #if (NGX_SSL) if (ngx_wasm_core_init_ssl(cycle) != NGX_OK) { ngx_wavm_destroy(vm); @@ -511,10 +483,6 @@ ngx_wasm_core_init_process(ngx_cycle_t *cycle) return NGX_ERROR; } - if (ngx_wasm_shm_init_process(cycle) != NGX_OK) { - return NGX_ERROR; - } - return NGX_OK; } diff --git a/src/wasm/ngx_wasm_directives.c b/src/wasm/ngx_wasm_directives.c index 9e0a7d5c4..0a51e308c 100644 --- a/src/wasm/ngx_wasm_directives.c +++ b/src/wasm/ngx_wasm_directives.c @@ -142,11 +142,11 @@ validate_shm_size(ngx_conf_t *cf, ssize_t size, ngx_str_t *value) return NGX_ERROR; } - if (size < (ssize_t) NGX_WASM_SHM_MIN_SIZE) { + if (size < (ssize_t) NGX_WA_SHM_MIN_SIZE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "[wasm] shm size of %z bytes is too small, " "minimum required is %z bytes", - size, NGX_WASM_SHM_MIN_SIZE); + size, NGX_WA_SHM_MIN_SIZE); return NGX_ERROR; } @@ -163,21 +163,21 @@ validate_shm_size(ngx_conf_t *cf, ssize_t size, ngx_str_t *value) static char * ngx_wasm_core_shm_generic_directive(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf, ngx_wasm_shm_type_e type) + void *conf, ngx_wa_shm_type_e type) { - size_t i; - ssize_t size; - ngx_str_t *value, *name, *arg3; - ngx_wasm_core_conf_t *wcf = conf; - ngx_wasm_shm_mapping_t *mapping; - ngx_wasm_shm_t *shm; - ngx_wasm_shm_eviction_e eviction; + size_t i; + ssize_t size; + ngx_str_t *value, *name, *arg3; + ngx_array_t *shms = ngx_wasmx_shms(cf->cycle); + ngx_wa_shm_mapping_t *mapping; + ngx_wa_shm_t *shm; + ngx_wa_shm_eviction_e eviction; value = cf->args->elts; name = &value[1]; size = ngx_parse_size(&value[2]); arg3 = (cf->args->nelts == 4) ? &value[3] : NULL; - eviction = NGX_WASM_SHM_EVICTION_SLRU; + eviction = NGX_WA_SHM_EVICTION_SLRU; if (!name->len) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -191,13 +191,13 @@ ngx_wasm_core_shm_generic_directive(ngx_conf_t *cf, ngx_command_t *cmd, if (arg3) { if (ngx_str_eq(arg3->data, arg3->len, "eviction=lru", -1)) { - eviction = NGX_WASM_SHM_EVICTION_LRU; + eviction = NGX_WA_SHM_EVICTION_LRU; } else if (ngx_str_eq(arg3->data, arg3->len, "eviction=slru", -1)) { - eviction = NGX_WASM_SHM_EVICTION_SLRU; + eviction = NGX_WA_SHM_EVICTION_SLRU; } else if (ngx_str_eq(arg3->data, arg3->len, "eviction=none", -1)) { - eviction = NGX_WASM_SHM_EVICTION_NONE; + eviction = NGX_WA_SHM_EVICTION_NONE; } else if (ngx_strncmp(arg3->data, "eviction=", 9) == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, @@ -213,7 +213,7 @@ ngx_wasm_core_shm_generic_directive(ngx_conf_t *cf, ngx_command_t *cmd, } } - shm = ngx_pcalloc(cf->pool, sizeof(ngx_wasm_shm_t)); + shm = ngx_pcalloc(cf->pool, sizeof(ngx_wa_shm_t)); if (shm == NULL) { return NGX_CONF_ERROR; } @@ -223,9 +223,9 @@ ngx_wasm_core_shm_generic_directive(ngx_conf_t *cf, ngx_command_t *cmd, shm->name = *name; shm->log = cf->cycle->log; - mapping = wcf->shms.elts; + mapping = shms->elts; - for (i = 0; i < wcf->shms.nelts; i++) { + for (i = 0; i < shms->nelts; i++) { if (ngx_str_eq(mapping[i].name.data, mapping[i].name.len, name->data, name->len)) { @@ -235,7 +235,7 @@ ngx_wasm_core_shm_generic_directive(ngx_conf_t *cf, ngx_command_t *cmd, } } - mapping = ngx_array_push(&wcf->shms); + mapping = ngx_array_push(shms); if (mapping == NULL) { return NGX_CONF_ERROR; } @@ -246,7 +246,7 @@ ngx_wasm_core_shm_generic_directive(ngx_conf_t *cf, ngx_command_t *cmd, return NGX_CONF_ERROR; } - mapping->zone->init = ngx_wasm_shm_init_zone; + mapping->zone->init = ngx_wa_shm_init_zone; mapping->zone->data = shm; mapping->zone->noreuse = 1; /* TODO: enable shm reuse (fix SIGHUP) */ @@ -302,7 +302,7 @@ char * ngx_wasm_core_shm_kv_directive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_wasm_core_shm_generic_directive(cf, cmd, - conf, NGX_WASM_SHM_TYPE_KV); + conf, NGX_WA_SHM_TYPE_KV); } @@ -326,7 +326,7 @@ ngx_wasm_core_shm_queue_directive(ngx_conf_t *cf, ngx_command_t *cmd, } return ngx_wasm_core_shm_generic_directive(cf, cmd, - conf, NGX_WASM_SHM_TYPE_QUEUE); + conf, NGX_WA_SHM_TYPE_QUEUE); } diff --git a/t/04-openresty/ffi/shm/001-setup_zones.t b/t/04-openresty/ffi/shm/001-setup_zones.t new file mode 100644 index 000000000..3b3bf0f3b --- /dev/null +++ b/t/04-openresty/ffi/shm/001-setup_zones.t @@ -0,0 +1,69 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(7); +run_tests(); + +__DATA__ + +=== TEST 1: shm - setup_zones() +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) + + assert(shm.inexistent_zone == nil) + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] +[stub] + + + +=== TEST 2: shm - setup_zones(), no zones +--- valgrind +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + assert(shm.metrics == nil) + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] +[stub] diff --git a/t/04-openresty/ffi/shm/002-iterate_keys.t b/t/04-openresty/ffi/shm/002-iterate_keys.t new file mode 100644 index 000000000..a1932914a --- /dev/null +++ b/t/04-openresty/ffi/shm/002-iterate_keys.t @@ -0,0 +1,182 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(6); +run_tests(); + +__DATA__ + +=== TEST 1: shm - iterate_keys() +--- valgrind +--- main_config + wasm { + shm_kv kv1 16k; + } +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local data = { k1 = "v1", k2 = "v2" } + local retrieved_keys = {} + + for k, v in pairs(data) do + shm.kv1:set(k, v, 0) + end + + shm.kv1:lock() + + for k in shm.kv1:iterate_keys() do + retrieved_keys[k] = true + end + + shm.kv1:unlock() + + assert(retrieved_keys.k1) + assert(retrieved_keys.k2) + assert(retrieved_keys.inexistent_key == nil) + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 2: shm - iterate_keys(), with batch size +--- valgrind +--- main_config + wasm { + shm_kv kv1 16k; + } +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local data = { k1 = "v1", k2 = "v2" } + local retrieved_keys = {} + local batch_size = 1 + + for k, v in pairs(data) do + shm.kv1:set(k, v, 0) + end + + shm.kv1:lock() + + for k in shm.kv1:iterate_keys(batch_size) do + retrieved_keys[k] = true + end + + shm.kv1:unlock() + + assert(retrieved_keys.k1) + assert(retrieved_keys.k2) + assert(retrieved_keys.inexistent_key == nil) + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 3: shm - iterate_keys(), no keys +--- valgrind +--- main_config + wasm { + shm_kv kv1 16k; + } +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.kv1:lock() + + for k in shm.kv1:iterate_keys() do + ngx.say("fail") + end + + shm.kv1:unlock() + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 4: shm - iterate_keys(), unlocked +--- valgrind +--- main_config + wasm { + shm_kv kv1 16k; + } +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + for k in shm.kv1:iterate_keys() do + end + } + } +--- error_code: 500 +--- error_log eval +qr/\[error\] .*? attempt to iterate over the keys of an unlocked shm zone. please call resty.wasmx.shm.kv1:lock\(\) before calling iterate_keys\(\) and resty.wasmx.shm.kv1:unlock\(\) after/ +--- no_error_log +[crit] +[emerg] +[alert] +[stub] + + + +=== TEST 5: shm - iterate_keys(), queue +--- valgrind +--- main_config + wasm { + shm_queue q1 16k; + } +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + for k in shm.q1:iterate_keys() do + end + } + } +--- error_code: 500 +--- error_log eval +qr/\[error\] .*? attempt to call method 'iterate_keys' \(a nil value\)/ +--- no_error_log +[crit] +[emerg] +[alert] +[stub] diff --git a/t/04-openresty/ffi/shm/003-get_keys.t b/t/04-openresty/ffi/shm/003-get_keys.t new file mode 100644 index 000000000..b5a16684f --- /dev/null +++ b/t/04-openresty/ffi/shm/003-get_keys.t @@ -0,0 +1,143 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(6); +run_tests(); + +__DATA__ + +=== TEST 1: shm - get_keys() +--- valgrind +--- wasm_modules: hostcalls +--- shm_kv: kv1 16k +--- config + location /t { + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k1 \ + value=hello1'; + + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k2 \ + value=hello2'; + + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local retrieved_keys = {} + + for _, k in ipairs(shm.kv1:get_keys()) do + retrieved_keys[k] = true + end + + assert(retrieved_keys["kv1/k1"]) + assert(retrieved_keys["kv1/k2"]) + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 2: shm - get_keys(), with limit +--- valgrind +--- wasm_modules: hostcalls +--- shm_kv: kv1 16k +--- config + location /t { + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k1 \ + value=hello1'; + + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k2 \ + value=hello2'; + + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local retrieved_keys = {} + local limit = 1 + + for _, k in ipairs(shm.kv1:get_keys(limit)) do + retrieved_keys[k] = true + end + + assert(retrieved_keys["kv1/k1"]) + assert(retrieved_keys["kv1/k2"] == nil) + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 3: shm - get_keys(), no keys +--- valgrind +--- main_config + wasm { + shm_kv kv1 16k; + } +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + assert(#shm.kv1:get_keys() == 0) + assert(#shm.kv1:get_keys(0) == 0) + assert(#shm.kv1:get_keys(1) == 0) + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 4: shm - get_keys(), queue +--- valgrind +--- main_config + wasm { + shm_queue q1 16k; + } +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.q1:get_keys() + } + } +--- error_code: 500 +--- error_log eval +qr/\[error\] .*? attempt to call method 'get_keys' \(a nil value\)/ +--- no_error_log +[crit] +[emerg] +[alert] +[stub] diff --git a/t/04-openresty/ffi/shm/101-kv_get.t b/t/04-openresty/ffi/shm/101-kv_get.t new file mode 100644 index 000000000..3e1b35656 --- /dev/null +++ b/t/04-openresty/ffi/shm/101-kv_get.t @@ -0,0 +1,90 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(6); +run_tests(); + +__DATA__ + +=== TEST 1: shm_kv - get() +--- valgrind +--- wasm_modules: hostcalls +--- shm_kv: kv1 16k +--- config + location /t { + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k1 \ + value=v1'; + + proxy_wasm hostcalls 'test=/t/shm/set_shared_data \ + key=kv1/k2 \ + value=v2'; + + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local expected = { ["kv1/k1"] = "v1", ["kv1/k2"] = "v2" } + + shm.kv1:lock() + + for k in shm.kv1:iterate_keys() do + local v, cas, err = shm.kv1:get(k) + assert(v == expected[k]) + assert(cas == 1) + end + + shm.kv1:unlock() + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 2: shm_kv - get(), unlocked +--- valgrind +--- main_config + wasm { + shm_kv kv1 16k; + } +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local initial_cas = 0 + local k = "k1" + local expected_v = "v1" + + local written, err = shm.kv1:set(k, expected_v, initial_cas) + assert(written == 1) + assert(err == nil) + + local v, cas, err = shm.kv1:get(k) + assert(v == expected_v) + assert(cas == initial_cas + 1) + assert(err == nil) + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] diff --git a/t/04-openresty/ffi/shm/102-kv_set.t b/t/04-openresty/ffi/shm/102-kv_set.t new file mode 100644 index 000000000..b440bb2f4 --- /dev/null +++ b/t/04-openresty/ffi/shm/102-kv_set.t @@ -0,0 +1,102 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(6); +run_tests(); + +__DATA__ + +=== TEST 1: shm_kv - set() +--- valgrind +--- main_config + wasm { + shm_kv kv1 16k; + } +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local values = { k1 = "v1", k2 = "v2" } + local cas = 0 + + for k, v in pairs(values) do + local written, err = shm.kv1:set(k, v, cas) + assert(written == 1) + assert(err == nil) + end + + for k, expected_v in pairs(values) do + local v, cas, err = shm.kv1:get(k) + assert(v == expected_v) + assert(cas == 1) + assert(err == nil) + end + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 2: shm_kv - set(), locked +--- valgrind +--- main_config + wasm { + shm_kv kv1 16k; + } +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local initial_values = { k1 = "v1", k2 = "v2" } + local expected_values = { k1 = "nv1", k2 = "nv2" } + local cas = 0 + + for k, v in pairs(initial_values) do + local written, err = shm.kv1:set(k, v, cas) + assert(written == 1) + assert(err == nil) + end + + shm.kv1:lock() + + for k in shm.kv1:iterate_keys() do + local written, err = shm.kv1:set(k, expected_values[k], cas + 1) + assert(written == 1) + assert(err == nil) + end + + shm.kv1:unlock() + + for k, expected_v in pairs(expected_values) do + local v, cas, err = shm.kv1:get(k) + assert(v == expected_v) + assert(cas == 2) + assert(err == nil) + end + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] diff --git a/t/04-openresty/ffi/shm/201-metrics_get.t b/t/04-openresty/ffi/shm/201-metrics_get.t new file mode 100644 index 000000000..4badfa365 --- /dev/null +++ b/t/04-openresty/ffi/shm/201-metrics_get.t @@ -0,0 +1,137 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(6); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - get, by name +--- valgrind +--- main_config + wasm {} +--- config + location /t { + access_by_lua_block { + local pretty = require "pl.pretty" + 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), + } + local values = {} + + shm.metrics:increment(metrics.c1) + shm.metrics:record(metrics.g1, 10) + shm.metrics:record(metrics.h1, 100) + + for name, _ in pairs(metrics) do + values[name] = shm.metrics:get_by_name(name) + end + + ngx.say("c1: " .. pretty.write(values.c1, "")) + ngx.say("g1: " .. pretty.write(values.g1, "")) + ngx.say("h1: " .. pretty.write(values.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] +[emerg] +[alert] + + + +=== TEST 2: shm_metrics - get, by name (prefix = false) +--- valgrind +--- wasm_modules: hostcalls +--- config + location /t { + proxy_wasm hostcalls 'on_configure=define_metrics \ + on=request_headers \ + n_increments=13 \ + test=/t/metrics/increment_counters \ + metrics=c1'; + + 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=10'; + + proxy_wasm hostcalls 'on_configure=define_metrics \ + on=request_headers \ + test=/t/metrics/record_histograms \ + metrics=h1 \ + value=100'; + + access_by_lua_block { + local pretty = require "pl.pretty" + local shm = require "resty.wasmx.shm" + + shm.metrics:lock() + + for name in shm.metrics:iterate_keys() do + local m, err = shm.metrics:get_by_name(name, { prefix = false }) + + assert(err == nil) + ngx.say(name .. ": " .. pretty.write(m, "")) + end + + shm.metrics:unlock() + } + } +--- response_body +pw.hostcalls.c1: {type="counter",value=13} +pw.hostcalls.g1: {type="gauge",value=1} +pw.hostcalls.h1: {type="histogram",value={{count=1,ub=16},{count=1,ub=128},{count=0,ub=4294967295}}} +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 3: shm_metrics - get, by name (invalid name) +--- valgrind +--- main_config + wasm {} +--- config + location /t { + access_by_lua_block { + local pretty = require "pl.pretty" + local shm = require "resty.wasmx.shm" + + local m, err = shm.metrics:get_by_name("invalid_name") + + assert(m == nil) + assert(err == "metric not found") + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] diff --git a/t/04-openresty/ffi/shm/202-metrics_define.t b/t/04-openresty/ffi/shm/202-metrics_define.t new file mode 100644 index 000000000..e6050f584 --- /dev/null +++ b/t/04-openresty/ffi/shm/202-metrics_define.t @@ -0,0 +1,106 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(9); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - define() +--- valgrind +--- main_config + wasm {} +--- 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 name, id in pairs(metrics) do + assert(type(id) == "number" and id > 0) + end + + ngx.say("ok") + } + } +--- response_body +ok +--- error_log eval +[ + qr/.*? \[info\] .*? defined counter "lua.c1" with id \d+/, + qr/.*? \[info\] .*? defined gauge "lua.g1" with id \d+/, + qr/.*? \[info\] .*? defined histogram "lua.h1" with id \d+/, +] +--- no_error_log +[error] +[crit] +[emerg] +[alert] + + + +=== TEST 2: shm_metrics - define(), name too long +--- valgrind +--- main_config + wasm {} +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local name_too_long = string.rep("x", 500) + local mid, err = shm.metrics:define(name_too_long, shm.metrics.HISTOGRAM) + + assert(mid == nil) + assert(err == "failed defining metric, name too long") + + ngx.say("ok") + } + } +--- response_body +ok +--- no_error_log +[error] +[crit] +[emerg] +[alert] +defined histogram +[stub] +[stub] + + + +=== TEST 3: shm_metrics - define(), invalid metric type +--- valgrind +--- main_config + wasm {} +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + shm.metrics:define("c1", 10) + } + } +--- error_code: 500 +--- error_log eval +qr/\[error\] .*? metric_type must be either resty.wasmx.shm.metrics.COUNTER, resty.wasmx.shm.metrics.GAUGE, or resty.wasmx.shm.metrics.HISTOGRAM/ +--- no_error_log +[crit] +[emerg] +[alert] +defined counter +[stub] +[stub] +[stub] diff --git a/t/04-openresty/ffi/shm/203-metrics_record.t b/t/04-openresty/ffi/shm/203-metrics_record.t new file mode 100644 index 000000000..16ee2b331 --- /dev/null +++ b/t/04-openresty/ffi/shm/203-metrics_record.t @@ -0,0 +1,71 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(6); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - record() +--- valgrind +--- main_config + wasm {} +--- config + location /t { + access_by_lua_block { + local pretty = require "pl.pretty" + local shm = require "resty.wasmx.shm" + + local g1 = shm.metrics:define("g1", shm.metrics.GAUGE) + local h1 = shm.metrics:define("h1", shm.metrics.HISTOGRAM) + + shm.metrics:record(g1, 10) + 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] +[emerg] +[alert] + + + +=== TEST 2: shm_metrics - record(), inexistent metric +--- valgrind +--- main_config + wasm {} +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local ok, err = shm.metrics:record(1, 10) + + if not ok then + ngx.say(err) + return + end + + ngx.say("fail") + } + } +--- response_body +metric not found +--- no_error_log +[error] +[crit] +[emerg] +[alert] diff --git a/t/04-openresty/ffi/shm/204-metrics_increment.t b/t/04-openresty/ffi/shm/204-metrics_increment.t new file mode 100644 index 000000000..fdd413a9b --- /dev/null +++ b/t/04-openresty/ffi/shm/204-metrics_increment.t @@ -0,0 +1,68 @@ +# vim:set ft= ts=4 sts=4 sw=4 et fdm=marker: + +use strict; +use lib '.'; +use t::TestWasmX; +use t::TestWasmX::Lua; + +skip_no_openresty(); + +plan_tests(6); +run_tests(); + +__DATA__ + +=== TEST 1: shm_metrics - increment() +--- valgrind +--- main_config + wasm {} +--- config + location /t { + access_by_lua_block { + local pretty = require "pl.pretty" + local shm = require "resty.wasmx.shm" + + local c1 = shm.metrics:define("c1", shm.metrics.COUNTER) + + shm.metrics:increment(c1) + 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] +[emerg] +[alert] + + + +=== TEST 2: shm_metrics - increment(), inexistent metric +--- valgrind +--- main_config + wasm {} +--- config + location /t { + access_by_lua_block { + local shm = require "resty.wasmx.shm" + + local ok, err = shm.metrics:increment(1, 10) + + if not ok then + ngx.say(err) + return + end + + ngx.say("fail") + } + } +--- response_body +metric not found +--- no_error_log +[error] +[crit] +[emerg] +[alert]