Hi Florian,

On 6 November 2015 at 16:02, Florian Maury <florian.ma...@ssi.gouv.fr> wrote:
> Hi everyone,
>
> While working on my Knot-Resolver (kr) module, I came to think that the
> current YIELD mechanism for layers will not work properly, if multiple
> YIELD-enabled modules are loaded simultaneously.
>
> My understanding of how the current YIELD system works is as follows.
> A layer receives the current state through ((knot_layer_t*) ctx)->state.
> The current state can be modified by previous layers returned value.
> When a layer returns a YIELD state, the chain of responsability,
> maintained by the lib/resolve.c:ITERATE_LAYERS macro is broken (break;)
> and the resolution plan is evaluated once more to handle the tail
> element of the rplan.
>
> Once the rplan popped up all rplan_pushed queries, then the query that
> yielded is once more the last element of the rplan stack. This element
> is then evaluated, calling once more all layers, starting with the
> first layer. This time, the state that is provided to the layers is
> YIELD, except if a layer returns another value instead of returning the
> state as is.

It doesn't evaluate *once more all* layers, but only the layer that
yielded, and all that follow.
For example, if the server has following:

LAYERS = { Iterator, Rrcache, Validator, Stats }
RPLAN = { Q1 }

The I,R are executed, Validator for Q1 yields and pushes Q2, Stats is
not called.
It looks like this.

RPLAN = { Q2, Q1[yielded at V] }

All layers are evaluated for Q2, and it pops out.

RPLAN = { Q1[yielded at V] }

Driver calls Validator on Q1 again with state YIELD, it returns normal state,
the next one is Stats which receives its state and finishes the resolution.

> A layer could behave differently, based on whether it receives a YIELD
> state or not as "input state". This is, in fact, what is provided as
> example in the documentation:
>
>>>>
> consume = function (state, req, answer)
>         answer = kres.pkt_t(answer)
>         if state == kres.YIELD then
>                 print('continuing yielded layer')
>                 return kres.DONE
>         else
>                 if answer:qtype() == kres.type.NS then
>                         req = kres.request_t(req)
>                         local qry = req:push(answer:qname(),
> kres.type.SOA, kres.class.IN)
>                         qry.flags = kres.query.AWAIT_CUT
>                         print('planned SOA query, yielding')
>                         return kres.YIELD
>                 end
>                 return state
>         end
> end
> <<<
>
> The issue, I think, is that a module has no way of knowing whether it is
> the one that yielded or not.

When it receives YIELD state, it is always the one which yielded.

> Let's say I have three module, A, B, and C. B and C can yield, and A
> only returns YIELD, when it receives YIELD as input state.

That's a cycle, it will yield to itself until the iteration limit is
reached and resolution is terminated.

> On the first evaluation, A and B returns DONE. C yields. Then on the
> second evaluation, A returns YIELD, because it does not handle this
> state and only passes it along. B receives the YIELD from A. B, as said

You can't pass along YIELD state, when a layer returns it, the layer
walk is suspended.
It's like when you're making coffee and realize there is none left, so
you leave the coffee maker and hot water in the kitchen and go
buy some more. When you get back you have everything prepared, so you
just put in the coffee you bought and finish.

> above, may yield in some cases. B will therefore handle the YIELD, and
> do his "YIELD" actions, ignoring the fact that the module that actually
> yielded was C.
>
> I suppose B and C could define flags to be capable of remembering
> whether they yielded on this query or not. Unfortunately, there is
> currently no framework to ensure uniqueness of these flags.
>
> Sadly, this email does not come up with a proposed solution. I'm only
> sharing a thought about what I think could be an issue.
>
> What is your take on this? Have I missed something or misunderstood how
> the yield state works?
>
> Thank you.
>
> Cheers,
> Florian

There is one problem similar to this - if you want to push new query
and intercept it, there's no unique way to mark it as "this is an
answer to query I'm waiting for".
You can use query flags like in DNS64 [1], but I'd like to do better
than that in the future.

Cheers,
Marek

[1]: 
https://github.com/CZ-NIC/knot-resolver/blob/master/modules/dns64/dns64.lua#L24

> _______________________________________________
> knot-dns-users mailing list
> knot-dns-users@lists.nic.cz
> https://lists.nic.cz/cgi-bin/mailman/listinfo/knot-dns-users
_______________________________________________
knot-dns-users mailing list
knot-dns-users@lists.nic.cz
https://lists.nic.cz/cgi-bin/mailman/listinfo/knot-dns-users

Reply via email to