• Stars
    star
    186
  • Rank 200,309 (Top 5 %)
  • Language
    Lua
  • License
    Apache License 2.0
  • Created over 8 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Cross Worker Events for Nginx in Pure Lua

Build Status

lua-resty-worker-events

Inter process events for Nginx worker processes

Table of Contents

Status

This library is production ready.

Synopsis

http {
    lua_package_path "/path/to/lua-resty-worker-events/lib/?.lua;;";

    # the size depends on the number of event to handle:
    lua_shared_dict process_events 1m;

    init_worker_by_lua_block {
        local ev = require "resty.worker.events"

        local handler = function(data, event, source, pid)
            print("received event; source=",source,
                  ", event=",event,
                  ", data=", tostring(data),
                  ", from process ",pid)
        end

        ev.register(handler)

        local ok, err = ev.configure {
            shm = "process_events", -- defined by "lua_shared_dict"
            timeout = 2,            -- life time of unique event data in shm
            interval = 1,           -- poll interval (seconds)

            wait_interval = 0.010,  -- wait before retry fetching event data
            wait_max = 0.5,         -- max wait time before discarding event
            shm_retries = 999,      -- retries for shm fragmentation (no memory)
        }
        if not ok then
            ngx.log(ngx.ERR, "failed to start event system: ", err)
            return
        end
    }

    server {
        ...

        # example for polling:
        location = /some/path {

            default_type text/plain;
            content_by_lua_block {
                -- manually call `poll` to stay up to date, can be used instead,
                -- or together with the timer interval. Polling is efficient,
                -- so if staying up-to-date is important, this is preferred.
                require("resty.worker.events").poll()

                -- do regular stuff here

            }
        }
    }
}

Description

Back to TOC

This module provides a way to send events to the other worker processes in an Nginx server. Communication is through a shared memory zone where event data will be stored.

The order of events in all workers is guaranteed to be the same.

The worker process will setup a timer to check for events in the background. The module follows a singleton pattern and hence runs once per worker. If staying up-to-date is important though, the interval can be set to a lesser frequency and a call to poll upon each request received makes sure everything is handled as soon as possible.

The design allows for 3 usecases;

  1. broadcast an event to all workers processes, see post. In this case the order of the events is guaranteed to be the same in all worker processes. Example; a healthcheck running in one worker, but informing all workers of a failed upstream node.
  2. broadcast an event to the local worker only, see post_local.
  3. coalesce external events to a single action. Example; all workers watch external events indicating an in-memory cache needs to be refreshed. When receiving it they all post it with a unique event hash (all workers generate the same hash), see unique parameter of post. Now only 1 worker will receive the event only once, so only one worker will hit the upstream database to refresh the in-memory data.

This module itself will fire two events with source="resty-worker-events";

  • event="started" when the module is first configured (note: the event handler must be registered before calling configure to be able to catch the event)
  • event="stopping" when the worker process exits (based on a timer premature setting)

See event_list for using events without hardcoded magic values/strings.

Troubleshooting

To properly size the shm, it is important to understand how it is being used. Event data is stored in the shm to pass it to the other workers. As such there are 2 types of entries in the shm:

  1. events that are to be executed by only a single worker (see the unique parameter of the post method). These entries get a ttl in the shm and will hence expire.
  2. all other events (except local events which do not use the SHM). In these cases there is no ttl set.

The result of the above is that the SHM will always be full! so that is not a metric to investigate at.

How to prevent problems:

  • the SHM size must at least be a multiple of the maximum payload expected. It must be able to cater for all the events that might be send within one interval (see configure).

  • no memory errors cannot be resolved by making the SHM bigger. The only way to resolve those is by increasing the shm_retries option passed to configure (which already has a high default). This is because the error is due to fragmentation and not a lack of memory.

  • the waiting for event data timed out error happens if event data gets evicted before all the workers got to deal with it. This can happen if there is a burst of (large-payload) events. To resolve these:

    • try to avoid big event payloads
    • use a smaller interval, so workers check for (and deal with) events more frequently (see interval option as passed to configure)
    • increase the SHM size, such that it can hold all the event data that might be send within 1 interval.

Methods

Back to TOC

configure

syntax: success, err = events.configure(opts)

Will initialize the event listener. This should typically be called from the init_by_lua handler, because it will make sure all workers start with the first event. In case of a reload of the system (starting new and stopping old workers) past events will not be replayed. And because the order in which workers reload cannot be guaranteed, also the event start cannot be guaranteed. So if some sort of state is derived from the events you have to manage that state separately.

The opts parameter is a Lua table with named options:

  • shm: (required) name of the shared memory to use. Event data will not expire, so the module relies on the shm lru mechanism to evict old events from the shm. As such the shm should probably not be used for other purposes.
  • shm_retries: (optional) number of retries when the shm returns "no memory" on posting an event, default 999. Each time there is an insertion attempt and no memory is available (either no space is available or the memory is available but fragmented), "up to tens" of old entries are evicted. After that, if there's still no memory available, the "no memory" error is returned. Retrying the insertion triggers the eviction phase several times, increasing the memory available as well as the probability of finding a large enough contiguous memory block available for the new event data.
  • interval: (optional) interval to poll for events (in seconds), default 1. Set to 0 to disable polling.
  • wait_interval: (optional) interval between two tries when a new eventid is found, but the data is not available yet (due to asynchronous behaviour of the worker processes)
  • wait_max: (optional) max time to wait for data when event id is found, before discarding the event. This is a fail-safe setting in case something went wrong.
  • timeout: (optional) timeout of unique event data stored in shm (in seconds), default 2. See the unique parameter of the post method.

The return value will be true, or nil and an error message.

This method can be called repeatedly to update the settings, except for the shm value which cannot be changed after the initial configuration.

NOTE: the wait_interval is executed using the ngx.sleep function. In contexts where this function is not available (eg. init_worker) it will execute a busy-wait to execute the delay.

Back to TOC

configured

syntax: is_already_configured = events.configured()

The events module runs as a singelton per workerprocess. The configured function allows to check whether it is already up and running. A check before starting any dependencies is recommended;

local events = require "resty.worker.events"

local initialization_of_my_module = function()
    assert(events.configured(), "Please configure the 'lua-resty-worker-events' "..
           "module before using my_module")

    -- do initialization here
end

Back to TOC

event_list

syntax: _M.events = events.event_list(sourcename, event1, event2, ...)

Utility function to generate event lists and prevent typos in magic strings. Accessing a non-existing event on the returned table will result in an 'unknown event error'. The first parameter sourcename is a unique name that identifies the event source, which will be available as field _source. All following parameters are the named events generated by the event source.

Example usage;

local ev = require "resty.worker.events"

-- Event source example

local events = ev.event_list(
        "my-module-event-source", -- available as _M.events._source
        "started",                -- available as _M.events.started
        "event2"                  -- available as _M.events.event2
    )

local raise_event = function(event, data)
    return ev.post(events._source, event, data)
end

-- Post my own 'started' event
raise_event(events.started, nil) -- nil for clarity, no eventdata is passed

-- define my module table
local _M = {
  events = events   -- export events table

  -- implementation goes here
}
return _M



-- Event client example;
local mymod = require("some_module")  -- module with an `events` table

-- define a callback and use source modules events table
local my_callback = function(data, event, source, pid)
    if event == mymod.events.started then  -- 'started' is the event name

        -- started event from the resty-worker-events module

    elseif event == mymod.events.stoppping then  -- 'stopping' is the event name

        -- the above will throw an error because of the typo in `stoppping`

    end
end

ev.register(my_callback, mymod.events._source)

Back to TOC

poll

syntax: success, err = events.poll()

Will poll for new events and handle them all (call the registered callbacks). The implementation is efficient, it will only check a single shared memory value and return immediately if no new events are available.

The return value will be "done" when it handled all events, "recursive" if it was already in a polling-loop, or nil + error if something went wrong. The "recursive" result simply means that an event-handler called poll again.

Back to TOC

post

syntax: success, err = events.post(source, event, data, unique)

Will post a new event. source and event are both strings. data can be anything (including nil) as long as it is (de)serializable by the cjson module.

If the unique parameter is provided then only one worker will execute the event, the other workers will ignore it. Also any follow up events with the same unique value will be ignored (for the timeout period specified to configure). The process executing the event will not necessarily be the process posting the event.

The return value will be true when the event was successfully posted or nil + error in case of failure.

Note: the worker process sending the event, will also receive the event! So if the eventsource will also act upon the event, it should not do so from the event posting code, but only when receiving it.

Back to TOC

post_local

syntax: success, err = events.post_local(source, event, data)

The same as post except that the event will be local to the worker process, it will not be broadcasted to other workers. With this method, the data element will not be jsonified.

The return value will be true when the event was successfully posted or nil + error in case of failure.

Back to TOC

register

syntax: events.register(callback, source, event1, event2, ...)

Will register a callback function to receive events. If source and event are omitted, then the callback will be executed on every event, if source is provided, then only events with a matching source will be passed. If (one or more) event name is given, then only when both source and event match the callback is invoked.

The callback should have the following signature;

syntax: callback = function(data, event, source, pid)

The parameters will be the same as the ones provided to post, except for the extra value pid which will be the pid of the originating worker process, or nil if it was a local event only. Any return value from callback will be discarded. Note: data may be a reference type of data (eg. a Lua table type). The same value is passed to all callbacks, so do not change the value in your handler, unless you know what you are doing!

The return value of register will be true, or it will throw an error if callback is not a function value.

WARNING: event handlers must return quickly. If a handler takes more time than the configured timeout value, events will be dropped!

Note: to receive the process own started event, the handler must be registered before calling configure

Back to TOC

register_weak

syntax: events.register_weak(callback, source, event1, event2, ...)

This function is identical to register, with the exception that the module will only hold weak references to the callback function.

Back to TOC

unregister

syntax: events.unregister(callback, source, event1, event2, ...)

Will unregister the callback function and prevent it from receiving further events. The parameters work exactly the same as with register.

The return value will be true if it was removed, false if it was not in the handlers list, or it will throw an error if callback is not a function value.

Back to TOC

Installation

Nothing special is required, install like any other pure Lua module. Just make sure its location is in the module search path.

Back to TOC

Bugs and Patches

Please report bugs or submit patches by creating a ticket on the GitHub Issue Tracker,

Back to TOC

Author

Thijs Schreijer [email protected], Kong Inc.

Back to TOC

Copyright and License

This module is licensed under the Apache 2.0 license.

Copyright (C) 2016-2020, by Thijs Schreijer, Kong Inc.

All rights reserved.

Back to TOC

History

Releasing new versions

  • make sure changelog below is up-to-date
  • update version number in the code
  • create a new rockspec in ./rockspecs
  • commit with message release x.x.x
  • tag the commit as x.x.x
  • push commit and tags
  • upload to luarocks

unreleased

  • chore: remove redundant type checking of unique_timeout.
  • chore: add stacktrace to post errors for better debug information.

2.0.1, 28-June-2021

  • fix: possible deadlock in the init phase

2.0.0, 16-September-2020

  • BREAKING: the post function does not call poll anymore, making all events asynchronous. When an immediate treatment to an event is needed an explicit call to poll must be done.
  • BREAKING: the post_local function does not immediately execute the event anymore, making all local events asynchronous. When an immediate treatment to an event is needed an explicit call to poll must be done.
  • fix: prevent spinning at 100% CPU when during a reload the event-shm is cleared
  • fix: improved logging in case of failure to write to shm (add payload size for troubleshooting purposes)
  • fix: do not log the payload anymore, since it might expose sensitive data through the logs
  • change: updated shm_retries default to 999
  • change: changed timer loop to a sleep-loop (performance)
  • fix: when re-configuring make sure callbacks table is initialized

1.1.0, 23-Dec-2020 (maintenance release)

  • feature: the polling loop now runs forever, sleeping for 0.5 seconds between runs, avoiding to create new timers on every step.

1.0.0, 18-July-2019

  • BREAKING: the return values from poll (and hence also post and post_local) changed to be more lua-ish, to be truthy when all is well.
  • feature: new option shm_retries to fix "no memory" errors caused by memory fragmentation in the shm when posting events.
  • fix: fixed two typos in variable names (edge cases)

0.3.3, 8-May-2018

  • fix: timeouts in init phases, by removing timeout setting, see issue #9

0.3.2, 11-Apr-2018

  • change: add a stacktrace to handler errors
  • fix: failing error handler if value was non-serializable, see issue #5
  • fix: fix a test for the weak handlers

Back to TOC

See Also

Back to TOC

More Repositories

1

kong

🦍 The Cloud-Native API Gateway and AI Gateway.
Lua
37,159
star
2

insomnia

The open-source, cross-platform API client for GraphQL, REST, WebSockets and gRPC.
JavaScript
30,407
star
3

unirest-java

Unirest in Java: Simplified, lightweight HTTP client library.
Java
2,560
star
4

kubernetes-ingress-controller

🦍 Kong for Kubernetes: The official Ingress Controller for Kubernetes.
Go
2,127
star
5

swrv

Stale-while-revalidate data fetching for Vue
TypeScript
2,048
star
6

mockbin

Mock, Test & Track HTTP Requests and Response for Microservices
JavaScript
1,988
star
7

mashape-oauth

OAuth Modules for Node.js - Supporting RSA, HMAC, PLAINTEXT, 2,3-Legged, 1.0a, Echo, XAuth, and 2.0
JavaScript
1,781
star
8

docker-kong

🐒 Docker distribution for Kong
Shell
1,351
star
9

unirest-php

Unirest in PHP: Simplified, lightweight HTTP client library.
PHP
1,282
star
10

httpsnippet

HTTP Request snippet generator for many languages & libraries
TypeScript
1,061
star
11

unirest-nodejs

Unirest in Node.js: Simplified, lightweight HTTP client library.
JavaScript
954
star
12

guardian

Remove the OAuth dance with one request.
JavaScript
640
star
13

unirest-python

Unirest in Python: Simplified, lightweight HTTP client library.
Python
432
star
14

deck

decK: Configuration management and drift detection for Kong
Go
419
star
15

apiembed

Embeddable API code snippets for your website, blog or API documentation
Pug
402
star
16

unirest-ruby

Unirest in Ruby: Simplified, lightweight HTTP client library.
Ruby
365
star
17

unirest-obj-c

Unirest in Objective-C: Simplified, lightweight HTTP client library.
Objective-C
276
star
18

kong-dist-kubernetes

Kubernetes managed Kong cluster
Shell
255
star
19

kong-plugin

Simple template to get started with custom Kong plugins
Lua
230
star
20

charts

Helm chart for Kong
Mustache
224
star
21

unirest-net

Unirest in .NET: Simplified, lightweight HTTP client library.
C#
190
star
22

docs.konghq.com

🦍 Source code for docs.konghq.com website.
Ruby
186
star
23

kong-oauth2-hello-world

This is a simple node.js + express.js application that shows an authorization page for the OAuth 2.0 plugin on Kong.
JavaScript
173
star
24

kong-manager

Admin GUI for Kong Gateway (Official)
TypeScript
170
star
25

lua-resty-dns-client

Lua DNS client, load balancer, and utility library
Lua
151
star
26

kong-pongo

Tooling to run plugin tests with Kong and Kong Enterprise
Lua
139
star
27

go-pdk

Kong Go Plugin Development Kit
Go
126
star
28

kong-vagrant

🐒 Vagrantfile for Kong testing and development
Shell
125
star
29

kongponents

🦍 Kong Vue Component Library
Vue
119
star
30

lua-resty-healthcheck

Healthcheck library for OpenResty to validate upstream service status
Lua
119
star
31

kong-plugin-prometheus

Prometheus plugin for Kong - this plugin has been moved into https://github.com/Kong/kong, please open issues and PRs in that repo
Lua
119
star
32

apiglossary

Open source glossary of API terms, acronyms and industry buzzwords.
95
star
33

go-plugins

A collection of Kong plugins written in Go
Go
86
star
34

go-kong

Go binding for Kong's admin API
Go
81
star
35

kong-terraform-aws

Kong Terraform Module for AWS
HCL
77
star
36

kong-build-tools

Build tools to package and release Kong
Shell
77
star
37

homebrew-kong

🐒 Homebrew tap for Kong
Ruby
69
star
38

kong-dist-cloudformation

🐒 Kong CloudFormation Stack
66
star
39

go-pluginserver

Kong Go Plugin Server
Go
66
star
40

ngx_wasm_module

Nginx + WebAssembly
C
64
star
41

kong-plugin-zipkin

A Kong plugin for propogating zipkin spans and reporting spans to a zipkin server - this plugin has been moved into https://github.com/Kong/kong, please open issues and PRs in that repo
Lua
60
star
42

kong-operator

Kong Operator for Kubernetes and OpenShift
Mustache
58
star
43

lua-multipart

Multipart Parser for Lua
Lua
55
star
44

mashape-php-library

Mashape PHP Server Library - Easily create an API in PHP. You can use it for existing services or brand new cloud components.
PHP
50
star
45

gojira

Multi-purpose tool to ease development and testing of Kong by using Docker containers
Shell
45
star
46

HARchiver

[Deprecated] Universal Lightweight Proxy for Galileo
OCaml
41
star
47

koko

koko - Control Plane for Kong Gateway [open-source]
Go
41
star
48

unirest-website

Simplified, lightweight HTTP libraries in multiple languages
HTML
39
star
49

kong-python-pdk

Write Kong plugins in Python
Python
39
star
50

go-srp

Secure Remote Password library for Go
Go
38
star
51

tcpbin

TCP Request & Response Service, written in node.js
HTML
37
star
52

kong-portal-templates

Themes, components, and utilities to help you get started with the Kong Dev Portal.
CSS
35
star
53

kong-plugin-acme

Let's Encrypt and ACMEv2 integration with Kong - this plugin has been moved into https://github.com/Kong/kong, please open issues and PRs in that repo
Lua
34
star
54

kong-mesh-dist-kubernetes

Start Kong 1.0 as a K8s sidecar
Makefile
33
star
55

kubernetes-testing-framework

Golang Integration Testing Framework For Kubernetes APIs and Controllers.
Go
32
star
56

gateway-operator

Go
32
star
57

demo-scene

🦍 a collection of demos and examples around Kong tools and technologies
JavaScript
30
star
58

docker-java8

A Dockerfile for starting a container with Java 8 installed
30
star
59

lua-kong-nginx-module

Nginx C module to allow deeper control of Nginx behaviors by Kong Lua code
Perl
30
star
60

Astronode-Broadcaster

A TCP replication server, or broadcaster, that replicates TCP commands to other TCP servers
Java
29
star
61

opentracing-lua

Opentracing Library for Lua
Lua
28
star
62

konnect-portal

Konnect OSS Dev Portal
TypeScript
28
star
63

atc-router

Expression based matching library for Kong
Rust
28
star
64

kong-js-pdk

Kong PDK for Javascript and plugin server
JavaScript
28
star
65

boss.js

Automatically load balance asyncronous jobs across multiple processes in a round-robin fashion.
JavaScript
27
star
66

kong-portal-cli

Kong Developer Portal CLI
TypeScript
25
star
67

lua-uuid

Lua library to generate UUIDs leveraging libuuid
Lua
25
star
68

insomnia-docs

This repository houses all Insomnia documentation.
JavaScript
25
star
69

lua-resty-lmdb

Safe API for manipulating LMDB databases using OpenResty/Lua.
C
24
star
70

lua-resty-aws

AWS SDK for OpenResty
Lua
22
star
71

lua-resty-timer

Extended timers for OpenResty
Perl
22
star
72

lua-resty-events

Inter process Pub/Sub pattern for Nginx worker processes
Perl
22
star
73

kong-plugin-request-transformer

Kong request transformer plugin - this plugin has been moved into https://github.com/Kong/kong, please open issues and PRs in that repo
Lua
21
star
74

lua-resty-counter

Lock-free counter for OpenResty
Perl
21
star
75

kong-plugin-session

🍪 Session plugin for Kong - this plugin has been moved into https://github.com/Kong/kong, please open issues and PRs in that repo
Lua
20
star
76

lua-pack

A library for packing and unpacking binary data.
C
20
star
77

go-apiops

Kong's Go based APIOps library
Go
18
star
78

swagger-ui-kong-theme

Plugin theme for Swagger-UI that adds snippets
JavaScript
18
star
79

api-log-format

Specification and examples of the new API logging format ALF
17
star
80

kong-plugin-serverless-functions

Kong Serverless Plugins - this plugin has been moved into https://github.com/Kong/kong, please open issues and PRs in that repo
Lua
17
star
81

apistatus

API status is a simple tool that checks if an API is online. http://apistatus.org
JavaScript
15
star
82

openresty-patches

Moved to https://github.com/Kong/kong-build-tools
Perl
14
star
83

kong-plugin-grpc-gateway

Kong Plugin to transcode REST request to gRPC - this plugin has been moved into https://github.com/Kong/kong, please open issues and PRs in that repo
Lua
14
star
84

lua-resty-consul-event

Consul Events HTTP API Wrapper
Perl
14
star
85

srp-js

Fork of node-srp modified to work in the browser
TypeScript
14
star
86

harplayer

Replay HAR logs
JavaScript
13
star
87

kong-plugin-aws-lambda

AWS Lambda plugin - this plugin has been moved into https://github.com/Kong/kong, please open issues and PRs in that repo
Lua
13
star
88

openresty-build-tools

Moved to https://github.com/Kong/kong-build-tools
Shell
13
star
89

jenkins-infrastructure

Cloudformation to create and update an ECS cluster that runs jenkins
Shell
12
star
90

kong-plugin-proxy-cache

HTTP Proxy Caching for Kong - this plugin has been moved into https://github.com/Kong/kong, please open issues and PRs in that repo
Lua
12
star
91

openapi2kong

Lib to convert OpenAPI specs into Kong specs
Lua
12
star
92

version.lua

Simple version comparison library
Lua
11
star
93

httpbin

Python
11
star
94

changelog-generator

a changelog generator focused on flexibility and ease of use
TypeScript
11
star
95

vault-kong-secrets

A Kong secrets backend for Vault
Go
11
star
96

py-postgrest

A library to work with PostgREST based APIs from Python
Python
11
star
97

priority-updater

Tool to quickly create a plugin with an updated priority
Lua
11
star
98

galileo-agent-java

Java Agent for Mashape Galileo
Java
10
star
99

kong-plugin-openwhisk

A kong plugin to invoke OpenWhisk action (serverless functions as service).
Lua
10
star
100

kong-upgrade-tests

Tests for upgrading from one Kong version to the next
Shell
10
star