-
Notifications
You must be signed in to change notification settings - Fork 378
VIP9B: Extend vmod object scopes
This proposal is the outcome of collaboration between Dridi and slink, influenced by reza and others:
We propose, in more detail than prior work, how vmod objects with additional scopes could be defined, implemented and managed.
We also propose new vcl subroutines for object initialization and to solve the requirement to copy or move objects from the client to the backend side.
The motivation is the discussion around the introduction of native variables to VCL and the extension of object scopes (PR #3140, VIP9)
We believe that this proposal shows ways how to achieve support for additional object scopes in a backwards compatible, safe and efficient manner.
In combination with VIP28 (designated vmod object methods), this is also intended as a basis for implementing variables in VCL.
We propose the following scopes for vmod objects:
-
vcl
aka globalsame as the only currently available scope
-
client
same as
PRIV_TASK
on the client side -
backend
same as
PRIV_TASK
on the backend side -
top
same as
PRIV_TOP
(only on the client side) -
local
like a local variable in C, scope is the current VCL sub
We propose the following new builtin subs
-
sub vcl_top_init {}
called once per client request at esi_level 0 before
vcl_recv
used for
top
object initialization, but not limited to itany non-local object constructed here will have
top
scope -
sub vcl_client_init {}
called once per client request before
vcl_recv
used for
client
object initialization, but not limited to itany non-local object constructed here will have
client
scope -
vcl_backend_init {}
called once per backend request halfway between the client and the backend side
here, both the
client
andbackend
scope objects are available (besidesreq
andbereq
), so any copy or move operations between the client and backend side can take place.any non-local object constructed here will have
backend
scope
Any object constructed in a non-init VCL sub will have local
scope and be usable only within that sub (something like VIP2 might allow to pass locally scoped objects down the stack).
VCC will provide a pointer to local
objects, but no space.
To allow for efficient allocations, VCC will extend support for the aws
(stack workspace): in each VCL sub, the aws
will get snapshot in the preamble and restored before each return, allowing vmods to make allocations from the aws
which follow the regular stack layout.
For failing aws
allocations, vmods should fall back to the regular task workspace.
local
scope vmod methods will need to ensure that any return values have at least PRIV_TASK
scope as before.
Edit 2019-11-23:
To clearly mark the limited scope of local
objects and to support them also in the *_init {}
subs, local
objects are defined by the local
keyword instead of new
.
Edit 2019-12-01:
local
need to be passed to the constructor, for example as a flag.
Currently, VCL loading fails if any declared objects remain uninitialized after vcl_init {}
returns, unless the constructor is declared with NULL_OK
in the vcc file.
We suggest to extend this concept to the new subs in that a VRT_fail()
error will be triggered at runtime for uninitalized client
/backend
/top
/scoped
objects unless the constructor is declared with NULL_OK
.
For use with local
scope, vmods are required to provide NULL_OK
support.
Note that NULL
refers here to the object, not the value of the object. In other words, a NULL
object and a method returning NULL
for access to an object (as it might be the case after an unset
) are different things.
By default, $Object
semantics remain unchanged for vcl scope.
Vmods supporting additional scopes make a $Scope
declarations in the VCC file as:
$Scope [any, vcl, top, client, backend, local, vcl_recv, vcl_deliver, ....]
This is a variant of the VIP4 $Restrict
keyword proposal. We would also include the remaining functionality of VIP4:
- The default
$Scope
isany
, except for$Object
, where it isvcl
-
$Method
can be$Scope
d or$Restrict
ed to a subset of the$Object
's$Scope
- We have no strong preference in naming,
$Scope
is probably a better fit
vmod object constructors determine the scope from the method
member of the VRT_CTX
.
Macros will be provided as convenience functions, for example:
SCOPE_VCL(ctx) SCOPE_CLIENT(ctx) SCOPE_BACKEND(ctx) SCOPE_TOP(ctx) SCOPE_LOCAL(ctx)
# MOCKUP VCL which is intended to demo VIP9B, but also contains syntax
# ideas from VIP28 - notice that VIP9B is viable without those, just
# the syntax would look different, e.g.:
#
# esi_fallback.set(tmp.get())
#
# instead of
#
# set esi_fallback = tmp
################################################################################
## TOP scope example
import file; # sub vcl_top_init {}
sub vcl_init {
new fallback = file.reader("fallback.txt");
}
sub vcl_top_init {
local tmp = var.string();
# constructing a string on the stack (aws) reduces workspace
# footprint, but tmp is really only intended as a demo here
set tmp = "<html>";
set tmp += fallback.get();
set tmp += "<br>Generated " + now + " for " + client.ip + "</html>";
# the var.string() getter returns the value on the workspace, construct
# a blob to (potentially) re-use in every esi subreq
new esi_fallback = var.blob();
set esi_fallback = tmp;
}
# for any initialization error, we would VRT_fail the request at this point
# (before vcl_recv is called)
sub vcl_deliver {
if (resp.status == 503 && req.esi_level > 0) {
set resp.body = esi_fallback;
}
}
################################################################################
## client / backend scope example
import directors;
backend foo None;
backend var None;
sub vcl_client_init {
new shard = directors.shard();
shard += foo;
shard += bar;
shard.reconfigure();
}
sub vcl_recv {
set req.backend_hint = shard.backend(by=KEY, key=1234);
}
sub vcl_backendend_init {
# creates a new backend object on the backend side from the client
# side object
new beshard = shard.inherit();
}
sub vcl_backend_fetch {
set bereq.backend = beshard;
}