[Tarantool-patches] [RFC 3/4] cfg: prepare symbolic evaluation of replication_synchro_quorum
Serge Petrenko
sergepetrenko at tarantool.org
Fri Nov 20 14:56:12 MSK 2020
20.11.2020 14:34, Cyrill Gorcunov пишет:
> On Fri, Nov 20, 2020 at 01:32:42PM +0300, Serge Petrenko wrote:
>>> +/**
>>> + * Evaluate replicaion syncro quorum number from a formula.
>>> + */
>>> +int
>>> +eval_replication_synchro_quorum(int nr_replicas)
>>> +{
>>> + const char fmt[] =
>>> + "local f, err = loadstring(\"return (%s)\")\n"
>>> + "if not f then return 'failed to load \"%s\"' end\n"
>>> + "setfenv(f, { n = %d })\n"
>>> + "local ok, res = pcall(f)\n"
>>> + "if not ok then return res end\n"
>>> + "return math.floor(res)\n";
>>> + char buf[512];
>>> + int value = -1;
>>> +
>>> + errno = 0;
>>> +
>>> + const char *expr = cfg_gets("replication_synchro_quorum");
>>> + size_t ret = snprintf(buf, sizeof(buf), fmt, expr,
>>> + expr, nr_replicas);
>>> + if (ret >= sizeof(buf)) {
>>> + errno = EINVAL;
>>> + diag_set(ClientError, ER_CFG,
>>> + "replication_synchro_quorum",
>>> + "the expression is too big");
>>> + return -1;
>>> + }
>>> +
>>> + luaL_loadstring(tarantool_L, buf);
>>> + lua_call(tarantool_L, 0, 1);
>>> +
>>> + if (lua_isnumber(tarantool_L, -1)) {
>>> + value = (int)lua_tonumber(tarantool_L, -1);
>>> + } else {
>>> + assert(lua_isstring(tarantool_L, -1));
>>> + errno = EINVAL;
>>> + diag_set(ClientError, ER_CFG,
>>> + "replication_synchro_quorum",
>>> + lua_tostring(tarantool_L, -1));
>>> + }
>>> + lua_pop(tarantool_L, 1);
>>> + return value;
>>> +}
>> Better make the function return -1 on an error, and when the function
>> evaluates to something negative,
>> log the eval result and return 1. This way you won't need errno.
> We need to distinguish wrong syntax and wrong evaluated values somehow
> which means using -1 for anything wont work here.
>
> - wrong syntax (which includes too long formula): -1 && errno
> - on correct syntax we simply return the result the caller has
> to check if returned value fits the allowed range.
>
> Or I'm missing something.
I was thinking that we're gonna return something like max(1, min(value, 31))
so that any evaluated number is correct. Lets better discuss this
verbally then.
>
>> You should either check every possible value, from 1 to VCLOCK_MAX - 1,
>> to make sure, say, that no division by zero is involved for some input.
> That's the good point. Another question if we should allow formulas like
> n-2, and while n <= 2 assume the quorum to be 1? Ie max(1, eval(n))
Yes, that's what I was speaking about above. So that when the formula may
be evaluated correctly (i.e. without division by zero or syntax errors) its
result will automatically be correct.
>
>> or check a single value, say, 1, or 2 or whatever, to make sure that the
>> expression in replication_synchro_quorum is at least valid Lua code.
>>
>> Why check only min and max inputs?
> Lets consider the evaluation a bit closer. Denote arguments as x, thus
> we will have x ∈ N, where are x's are strictly ordered, thus to get
> f(x) < 0 between min/max values we need to use nonlinear f. I must confess
> I don't know what is allowed inside Lua evaluator and really don't like
> that we're allowing to pass completely arbitrary formula here. Moreover
> what would happen if f would be using some weird data so that if will be
> linear on bootstrap procedure but then switched to something more complex?
> All this arbitrary evaluation looks very very fishy and I bet there will
> be problems because this is the data we do not control completely.
I guess we shouldn't be this crazy about what is allowed in this formula
and what's not.
If a user has access to box.cfg{}, he may evaluate any expression he
wishes anyway.
Anyway, this is subject of a verbal discussion.
>
> Back to the former question -- initially I assume the f gonna be linear
> and eval in min/max will be enough. But of course this is not correct.
>
> You know I can pass all N's here but still this doesn't guarantee anything :(
> That's why I'm for more strict rules here:
>
> - allow some symbolic names such as
> "all" -> (alias for f(x) = n)
> "canonical" -> (alias for f(x) = n/2 + 1)
Sounds good to me. AFAIR others were agains it, though.
>
> - implement own syntax analizyer/evaluator which would allow only
> basic operation {+,/} over the set of {n} (which will give us
> a linear functioni by definition). This of course will require
> a way more code so not right now.
>
>>> @@ -368,7 +368,6 @@ local dynamic_cfg_skip_at_load = {
>>> replication_connect_quorum = true,
>>> replication_sync_lag = true,
>>> replication_sync_timeout = true,
>>> - replication_synchro_quorum = true,
>> You shouldn't remove replication_synchro_quorum from here.
>> This table lists the options which are set directly from `box_cfg` in
>> specific order.
> No, this table is to _skip_ evaluation on bootup. But we have to verify
> the default value to evaluate.
Yes, that's what I'm talking about.
Even if the cfg option from this list is 'skipped' in lua, it's
referenced
directly from box_cfg_xc(). Othervise the `box_cfg_set_...` will be
called twice.
Once from box_cfg_xc(), second time from this lua code.
To be more verbose, all the setters from dynamic_cfg_skip_at_load are
called on
bootstrap. But from box_cfg_xc() in C, not from Lua. If you remove an entry
from dynamic_cfg_skip_at_load, the corresponding setter will be called
twice.
>
>> I.e. not lua calls box_cfg_set_..., but box_cfg() itself does. But only on
>> the first box.cfg call.
--
Serge Petrenko
More information about the Tarantool-patches
mailing list