[swift-server-dev] Next HTTP API meeting

Paulo Faria paulo at zewo.io
Tue Mar 28 07:26:28 CDT 2017


Again, I'm with Brent a 100%. Couldn't have expressed my thoughts better,
haha.

Also, I see people mentioning async IO and threads as if they're the only
way. We have to remember that there are other options like coroutines which
allow non-blocking synchronous APIs. All of these things are concerns of
higher level frameworks and while the low level framework must allow them,
the design itself shouldn't make an assumption of a specific concurrency /
IO model. We should think about HTTP messages from the POV of the parser
here. Not the high level framework. This is exactly the high level
framework's job; to wrap low level stuff in the way it sees fit.

If we go back to the basics and observe HTTP messages from the point of
view of the low level protocol we see that incoming HTTP messages can be
represented simply by a header and a body stream. I can easily see the
request line/status line and headers being represented by a value type and
the body being represented by a reference type when the message is in the
representation of an incoming message.

This incoming message representation should allow the body stream to be
implemented with two distinct APIs, synchronous and asynchronous APIs, and
use different concurrency models. My guess is that Kitura and Vapor would
use async APIs through libdispatch, Perfect would use async APIs throught
its custom async thread-based concurrency model and Zewo would use
non-blocking synchronous APIs using libdill. Some other framework might
choose to implement blocking synchronous APIs for some reason too. Although
the IO model is different, our concern is about APIs, and in this case we
have two sets of APIs, synchronous APIs and asynchronous APIs. This means
that we would need two protocols for the HTTP body. One for synchronous
streams and one for asynchronous streams. Maybe this means we would need a
parser for each kind of API. A synchronous parser which produces HTTP
messages with the body represented as a synchronous stream and an
asynchronous parser which produces HTTP messages with the body represented
as asynchronous streams. Or we could have a single parser which would
produce either kind of HTTP message through configuration parameters. I
think maybe the best option would be two parsers, specially because I think
some of the parsers' API would have to differ.

While the text above mentions HTTP messages in its incoming form (and
that's what I think we should focus on for now, since we're talking about
the parser) there's also the representation of a fully-formed HTTP message
which is a concern of high-level frameworks. It seems this is what is
mostly being discussed here. I think for now we shouldn't discuss this.
While I do think having a single high level representation being used by
all frameworks would be simply amazing, like Rack was mentioned. I think we
should go one step at a time. So I think, let's focus on the parser POV
which is not the high level framework POV. Let's agree on a base and let
the frameworks wrap and expose whatever they want now. Later we can unify
high level representations and middleware, if possible at all.

On Tue, Mar 28, 2017, 08:22 Brent Royal-Gordon <brent at architechies.com>
wrote:

> > On Mar 27, 2017, at 5:13 AM, Logan Wright <logan at qutheory.io> wrote:
> >
> > I disagree with the premise that protocols inherently create bugs
>
> To be clear, I am *not* saying that they inherently create bugs. What I'm
> saying is that they *often* create bugs *if* they contain mutating members
> *and* do not specify whether they expect value or reference semantics.
>
> Here's an example: `RangeReplaceableCollection` does not specify whether
> conforming types should have value or reference semantics. As a result,
> there's a heartstopping FIXME in the middle of the `+` operator's
> implementation:
>
>         public func +<
>           RRC1 : RangeReplaceableCollection,
>           RRC2 : RangeReplaceableCollection
>         >(lhs: RRC1, rhs: RRC2) -> RRC1
>           where RRC1.Iterator.Element == RRC2.Iterator.Element {
>
>           var lhs = lhs
>           // FIXME: what if lhs is a reference type?  This will mutate it.
>           lhs.reserveCapacity(lhs.count + numericCast(rhs.count))
>           lhs.append(contentsOf: rhs)
>           return lhs
>         }
>
> *This is the `+` operator!* It ships with Swift! And it will do the wrong
> thing if you use it with a reference type, because it's very difficult to
> write a generic algorithm with mutating operations which does the *right*
> thing when it's not sure whether the type provides value or reference
> semantics. It's possible, but tricky, to do it when you create the
> instances and completely control them right until the moment when you
> finally hand them off to the caller—you basically just have to pretend it's
> a move-only type. It's virtually impossible when you have to mutate
> something somebody passed to you.
>
> That's why this:
>
> > If we create protocols, this allows frameworks to implement concrete
> models with whatever semantics they see fit. Any subsequent interaction
> from the framework would have a concrete type to rely on.
>
> Doesn't really work. People can't reliably use your type if they don't
> know what semantics to expect.
>
> > If people feel extremely strong that there needs to be a concrete type,
> then I'd like to push for reference type as much as possible. As far as
> reference vs value type, I haven't really heard an argument for value types
> beyond what feels like a reaction to value types being the hip new hotness.
> While yes, they're great in Swift, and there's tons of places that should
> absolutely be modeled with value semantics, a request/response interaction
> represents a single request and should definitely be a reference based
> interaction.
>
> Here's the argument for value semantics:
>
> 1. A good web framework is going to have several different, decoupled
> components written by different teams. If you have shared mutable state,
> then a mutation by one component will be seen by the other components,
> which may not be expecting it.
>
> 2. A good web framework will almost certainly be concurrent. If you have
> shared, mutable state *with concurrency*, then you have to have to
> synchronize access to that state or your users will spend the rest of their
> lives squashing Heisenbugs. (Thread confinement is a synchronization
> strategy, of course.)
>
> With reference types, it is possible for careful programmers to avoid bugs
> caused by mutating shared mutable state; with value types, this safety is
> automatic and there simply isn't any reason to worry about these kinds of
> bugs.
>
> The request and response are probably going to be very widely distributed.
> That means there's a *lot* of code which could screw things up for
> everybody else. If the request and response are mutable, giving them value
> semantics is likely to reduce bugs by removing a vector for two pieces of
> code to interact poorly.
>
> > In practice, we went through several model iterations and the value type
> created very high levels of bugs and confusion for end users. The three
> biggest problems we had were as follows:
> >
> > - Greatly increased bug levels and confusion related to unexpected
> mutation
>
> Can you explain the sort of bugs you saw in more detail? I have an easy
> time understanding how the "action-at-a-distance" behavior of a reference
> type would cause bugs. I have a much harder time understanding how
> mutations *not* being seen elsewhere would cause bugs, other than "nothing
> really misbehaved, I just didn't know how this works".
>
> > - Unnecessary code requirements added to every single passive access
> (ie: middleware) increasing code bloat unnecessarily
>
> What does "unnecessary code requirements" mean here?
>
> > - Extreme performance loss due to massive copy overhead
>
> Did you attempt to reduce copying overhead by making your value types COW
> wrappers? If so, what was your experience with that approach?
>
> --
> Brent Royal-Gordon
> Architechies
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-server-dev/attachments/20170328/b5451f83/attachment.html>


More information about the swift-server-dev mailing list