objcore/req symmetry and Obj->methods

Poul-Henning Kamp phk at phk.freebsd.dk
Thu Nov 26 11:31:06 CET 2015


(This is rather long and rambling and probably mostly for Martin,
but I'm sending it -dev in case anybody else is interested and for
completeness of the mail-archive.)

In the past, varnishd has been very asymmetric:  We had requests
going one way and objcore going the other and they were very
different.

Being asymmetric saved us a lot of code initially, we didn't do
req.body unless we piped, that saved a lot of code.

Strictly speaking the HTTP protocol does not see requests and
responses as particularly different, there's that first "envelope"
line, and a lot of detailed semantic rules, but overall they're
not different.

We have moved towards a higher degree of symmetry in the last
release cycle, for instance we use VFP's to slurp in the req.body
and we store it in a (private) objcore.

In the future we will see more pressure for symmetry, for instance:

	https://tools.ietf.org/html/draft-thomson-http-encryption

(Perfectly sensible proposal and great topic for a (series of?) VMODs)

So I have been staring at cache_obj.c and cache_busyobj.c a lot, for
the "no-storage-pass", "streaming-end-trim" and #1788 issues, and
concluded that something needs to happen, so we don't have to solve
these issues separately for both directions.

We already have such two-of-a-kind solutions, for instance:

By default we 'stream' req.body, but we have a mechanism for "caching"
it.  How is this different from streaming responses, but having a
way to say 'no_stream' ?

A stevedore can override the Obj methods, but only at the cost of
replicating all the rather incestuous objcore/busyobj code which
makes streaming work.

That code is highly irregular, for instance ObjIter knows about
busyobj, but ObjExtend() does not etc.

I don't want to see the necessary logic for busyobj/streaming
replicated in all advanced stevedores, so the present API will
not do.

This raises the interesting question:  Should we always use a busyobj
to fill an objcore, even for req.body and synthetic ?

I'm increasingly leaning that way, for reasons of code simplicity
and symmetry.

We have people asking for the "send the request to N backends and
use the first response you get" functionality - which is the exact
same problem as obj.body streaming is, so for req.body there is
a plain use-case.

Streaming on synthetic seems far out only until you start using
weird VMODs to generate the synthetic response body slowly[1].

...so assume for a moment we always use a busyobj and the streaming
mechanism to fill objcores.

As long as the busyobj is assembling the objcore, *all* the Obj->methods
go to the busyobj's method set, and the busyobj methods call the
stevedores methods to get things done.

Once the busyobj is done, it calls the stevedore with a "commit"
method and once that returns, all future Obj->methods go directly
to the stevedore.

... Except the ObjIter()s which were already running, we cannot
allow the stevedore to rip storage out or move it under them,
and they have to finish in the busyobj state.

I can imagine three stevedore stragegies:

1. Can do Trim in place, so allocates final storage right away
   and trims last segment when we know it.

2. Allocates temp space for each segment, moves into final storage
   when it is full/finished.

3. Allocates all segments in temp space, and moves them all into
   final storage only when object is finished.

But I'm going to deny #3, because the performance difference between
#2 with "large enough" segments and #3 is not relevant.

So the stevedore gets to pick between strategy #1 and #2 for each
object.  The decision, and the #2 segment size, might be based on
knowing the length from C-L or having an estimate from objhdr.

This means we have at most one "uncommitted" segment at any time,
and if we let the busyobj own that one (from Transient), the ObjIter
issue is manageable.

Fitting the "delete while passing" case into this, is a matter of
bookkeeping and calling the stevedores ->delete_segment method.

Comments, inputs, thoughts ?

Poul-Henning


[1] Actually, it's not obvious to me that we should have the "magic"
    path for synthetic any more, why don't we just push a synthetic
    backend and use the regular path ?


-- 
Poul-Henning Kamp       | UNIX since Zilog Zeus 3.20
phk at FreeBSD.ORG         | TCP/IP since RFC 956
FreeBSD committer       | BSD since 4.3-tahoe
Never attribute to malice what can adequately be explained by incompetence.



More information about the varnish-dev mailing list