You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/features/proxy/api-reference.md
+250-61
Original file line number
Diff line number
Diff line change
@@ -6,11 +6,15 @@ weight = 200
6
6
7
7
This page provides a description of and reference to the Memcached built-in proxy API. These allow customization or replacement of the proxy's standard route library in advanced use cases.
8
8
9
+
Use this reference if you wish to make changes to the standard route library,
10
+
extend it with your own custom route handlers, or write your own fully custom
11
+
library from scratch.
12
+
9
13
For a general overview of the built-in proxy, see [Built-in proxy]({{<proxy_base_path>}}).
10
14
11
15
## Development status of the proxy API
12
16
13
-
At this stage API functions are mostly stable, but are still subject to
17
+
API functions are mostly stable, but are still subject to
14
18
occasional change. Most changes in functionality will be either additions or
15
19
with a backwards-compatible deprecation cycle.
16
20
@@ -51,102 +55,289 @@ flowchart TD
51
55
configure --> |for each worker: copy lua code and config table|worker
52
56
```
53
57
54
-
The proxy flow starts by parsing a request (ie: `get foo`) and looking for a function hook for this command. If a hook exists, it will call the supplied function. If no hook exists, it will handle the request as though it were a normal memcached.
58
+
## Configuration stages
59
+
60
+
As noted above, configuring the proxy happens in
61
+
two distinct stages. These stages use fully independent Lua VM instances: a
62
+
configuration instance, and one for each worker thread. This separation is
63
+
designed to improve the safety of reloading the config and for performance as
64
+
a separate "configuration thread" is used for more intense work.
65
+
66
+
If a configuration fails to execute on reload, in most cases, the proxy will
67
+
continue to run the old configuration.
68
+
69
+
The same Lua code file is used for all threads: it is loaded and compiled
70
+
once.
55
71
56
-
In Lua, this looks like: `mcp.attach(mcp.CMD_GET, function)` - Functions are objects and can be passed as arguments. The function is called within a coroutine, which allows us to designs routes procedurally even if they have to make a network call in the middle of executing.
72
+
The first stage is to call `mcp_config_pools` from the "configuration thread".
73
+
This stage defines backends and organizes them into pools. It is also a good
74
+
place to decide on how keys and commands should be routed. A table tree of
75
+
information is then returned from this function. The proxy is not blocked or
76
+
otherise impacted while this function runs.
77
+
78
+
The second stage is to call `mcp_config_routes` from each "worker thread".
79
+
These VM's run independently for performance reasons: there is no locking
80
+
involved and any garbage collection happens per-thread instead of for the
81
+
whole process.
82
+
83
+
In this second stage we take information generated in the first stage to
84
+
create function generator objects, which produce coroutines that actually
85
+
process user requests. We also generate router objects that decide which
86
+
functions should handle a particular command or key prefix. Finally, we attach
87
+
either a function generator or a router object to the proxy's command hooks.
88
+
89
+
Since this second stage is executed in the same worker threads that handle
90
+
requests, it is a good idea to keep the code simple. Errors here can also
91
+
stop the process and are not easily recoverable.
57
92
58
-
The function is called with a prototype of:
59
93
```lua
60
-
function(request)
94
+
functionmcp_config_pools()
95
+
-- create a table of pools, and route information
96
+
localt= {}
97
+
-- fill t with pools/information
98
+
returnt
99
+
end
61
100
101
+
functionmcp_config_routes(t)
102
+
-- Create function generators and routers.
103
+
localrouter=-- see below
104
+
-- Here we override any command. It is also possible to override only
105
+
-- specific commands: ie: just override 'set', but handle 'get' as though
106
+
-- we are not a proxy but a normal storage server.
107
+
mcp.attach(mcp.CMD_ANY_STORAGE, router)
62
108
end
63
109
```
64
110
65
-
The most basic example of a valid route would be:
66
-
`mcp.attach(mcp.CMD_GET, function(r) return "SERVER_ERROR no route\r\n" end)`
can handle this for you and is a good abstraction to start from. For most
114
+
people the rest of this API reference can be used to create your own route
115
+
handlers. Since routelib handles the stages of execution, you can focus on
116
+
processing requests and nothing else about how the configuration is built.
67
117
68
-
For any get command, we will return the above string to the client. This isn't very useful as-is. We want to test the key and send the command to a specific backend pool; but the function only takes a request object. How are routes actually built?
118
+
---
69
119
70
-
The way we recommend configuring routes are with _function closures_. In lua functions can be created capturing the environment they were created in. For example:
- look for files that start with `custom`. The files are commented. More
125
+
detailed information is repeated here.
126
+
127
+
### Loading custom handlers
79
128
80
-
mcp.attach(mcp.CMD_GET, new_route())
129
+
You must instruct memcached to load your custom route handler files. If you
130
+
are already using routelib your start line may look like:
131
+
132
+
```
133
+
-o proxy_config=routelib.lua,proxy_arg=config.lua
81
134
```
82
135
83
-
In this example, `new_route()`_returns a function_. This function has access to the environment (`local res = `) of its parent. When proxy calls the `CMD_GET` hook, it's calling the function that was returned by `new_route()`, not `new_route()` itself. This function uselessly returns a string.
136
+
If your custom route handler is defined in `customhandler.lua`, you need to
137
+
add it as an argument to `proxy_config`:
84
138
85
-
This should give you enough context to understand how the libraries in [the proxylibs repository](https://github.com/memcached/memcached-proxylibs) are implemented.
This lets the proxy manage loading and reloading the configuration for you,
144
+
without having to focus too much on Lua.
86
145
87
-
Since we have a real programming language for both configuration and the routes themselves, we can write loops around patterns and keep the configuration short.
146
+
### Custom handler configuration stages
88
147
89
-
## Function generators and request contexts
148
+
As described above in [configuration stages](#configuration-stages) there is a
149
+
two-stage process to configuration loading. This needs to be taken into
150
+
account when writing custom handlers for routelib as well.
90
151
91
-
NOTE: This information is only useful for people intending to develop a route
92
-
library or extend `routelib`. End users should read the
0 commit comments