Thank you for your interest. I've been distracted with other things for a little bit too long, and will be for a few weeks more, but let's try to get this thing back on tracks.
These are the current biggest roadblocks, and experimenting with solutions to them has been consuming the hours that would have gone into Bazil proper. These are mostly building blocks that are missing or inadequate, and thus Bazil itself has not received as much attention yet.
This list is a bit off-the-cuff, so I may be forgetting something major. Apologies for being scatterbrained right now. I've been quiet because I haven't been able to formulate this more coherently, but I realize too much silence is not a good thing, so let's try this for now. Each one of these topics alone is worth a long blog post and/or 1 hour talk, and I've been trying to avoid going too deep into any single one of them, hoping that suitable libraries show up from elsewhere -- that largely hasn't happened, yet.
ROADBLOCKS
1. A good bidirectional protocol / messaging system that will survive in the greater Internet
(Protocol, not a message queue. And please do not suggest 0mq.)
Not a request-response RPC mechanism, but one where peers can stream messages at each other freely. See my birpc experiment for inspiration, though JSON will absolutely not be good enough, and constructing a more complex dialogue of RPC calls gets frustrating, with continuation tokens or such..
https://github.com/tv42/birpc/
Low overhead per message. Crypto-safe; we'll know the peers nacl/box public key. Can be TLS, but cannot force user to understand CAs; must bootstrap from the before-mentioned known public key. Must avoid head-of-line blocking, and must do some sort of backpressure to control flow; we need to multiplex large data transfers with interactive requests without using too many TCP/IP connections.
I've been looking at layering on top of HTTP, to have the URL path differentiate between API endpoints, handle versioning, etc. Add something to prove TLS key is owned by nacl/box key, both ways. But REST really doesn't work for complex protocols. I would also like to avoid the XOR cost of WebSocket, so I've been looking at Upgrade headers and essentially using HTTP for handshake only. And WebSocket explicitly doesn't do anything about head-of-line blocking, so I'd end up treating it as a stream transport only anyway, so what's the point.
But HTTP Upgrade: will fail for anyone behind a strict proxy, and just devolves into a TCP/TLS protocol from scratch: it won't fulfill the "use URL path to differentiate" goal from earlier, as there's no good way to go *back* from the ugprade to speaking HTTP -- so it really doesn't tie in with HTTP well at all (even if proxy switched to pure-TCP passing when it saw the Upgrade:, now it won't know when we're back in HTTP land.. imagine trying to put that behind a load balancer). Maybe we don't care about proxies? Maybe the HTTP thing is just a distraction. It is fashionable..
I have a couple of half-tolerable from-scratch protocols (whether to be run inside HTTPS Upgrade:, or WebSocket, or not), but nothing good enough. One source of inspiration has been IX:
http://lsub.org/ls/export/creepy.pdf
I'd really like to avoid this work, but I don't see a real contender out there. Right now I have a sketch of a protocol with "streams" in both directions, and each "stream" is a conversation that's semantically a superset of an RPC call. Whether that piggybacks on top of HTTP+Upgrade or not is still open.
Anyone really familiar with SPDY? Could it behave like a non-retarded HTTPS+WebSocket for me? Is it too tied to HTTP semantics? Are the Go libraries good enough already? It's all been too much in flux for me to keep track of.
Is the XOR cost of WebSocket even relevant if it sits inside TLS anyway? Each byte gets touched for the crypto anyway.
Anyone familiar with bootstrapping TLS into two-way trust *without* the CA dance? I wrote a proof-of-concept thing that handshakes at the beginning inside an anything-goes TLS connection, but haven't audited the it much against MITM and e.g. how to recover from accidental key disclosure. Once again, peers know each others public keys (nacl/box not TLS).
Long term, I'd love to have the large data transfers go over CurveCP+LEDBAT or something like that; yield to normal (TCP-like) traffic.
Once this is solidly in place, plugging in the file synchronization logic is much easier.
2. On top of the above, "endpoints" would basically build a little protocol within their allotted stream, e.g. a file tree sync looks like (using mscgen):
msc {
// A pulls from B
a, b;
a->b [label="list(dirPath)"];
a<-b [label="(basename, vPair, {file: manifest; dir: -})"];
... [label="<above repeats>"];
a<-b [label="endOfDir"];
}
Except it might actually have multiple of *these* little dialogs multiplexed & interleaved inside one "synchronization". (The originating side decides which subtrees to recurse into, based on the vPair version vectors it sees in the parent directory listing. This proceeds pretty much concurrently across the whole tree. Yet these are all part of a single sync event, so the bookkeeping costs are shared across them all.)
So whereas with RPC you have method+request data -> response data or error, with this you get a little dialog for every "RPC call", all multiplexed over a single connection. I'm thinking this layer cares about serialization formats, where as 1. above is just shuffling streams around. This probably needs some sort of library/framework help, akin to net/rpc.
3. Help OSXFUSE release a new stable version, with a newer version of the FUSE protocol.
I want to migrate to the newer protocol on Linux, for performance, but I'm wary of splitting the codebase into multiple variants.
4. Semi-unrelated but taking up time,
bazil.org/fuse Serve/Mount/fs.Init API needs to be replaced with something cleaner. Think about both Linux and OS X, and how their behavior is completely different. Don't take away too much power from pure protocol translators that don't want to use
bazil.org/fuse/fs. Worry about InitResponse.MaxWrite ultimately being a question of
bazil.org/fuse, not the filesystem, etc.
5.
bazil.org/fuse on linux just isn't fast enough. CPU profiles are pretty evenly smeared across I recall 3 major groups, no single easy bottleneck. Use the newer protocol and vmsplice? Needs re-work of the
bazil.org/fuse ReadRequest level; one []byte will be backing multiple incoming requests.
To avoid gc churn, that large []byte should be in a sync.Pool. That means it needs to stay alive long enough for all of the handlers to complete, before sync.Pool.Put; so there needs to be e.g. a refcount on the larger []byte, and then sync.Pool.Put when it drops to zero.
Also, current handlers may hold on to e.g. WriteRequest.Data even after returning; that's equivalent to C's use-after-free bug, and nasty things will happen. (Putting the sync.Pool.Put behind a const conditional will help in debugging, but that just means going back to a whole lot of gc churn.)
1000. Something better than protocol buffers. Fast access to fields of a message inside a read-only []byte (mmapped memory). Capnproto (as used from Go, with the current lib) is promising until the moment you actually try to access the data, and then it's even slower than gogoprotobuf. And the APIs are all semi-hideous too.
I'd be willing to trade off some of the flexibility guarantees of the protobuf design. I'm halfway-tempted to just unsafe.Pointer a struct on top of the mmapped []byte.
General things you can study, to contribute:
- Go. Duh.
- Tra, the file syncer; especially the algorithm
I'll write up more of this and/or do talks/hangouts or something to spread the word more; I just need to deal with a couple of other things before I'm ready for that. Please ask good questions, they really help guide me into explaining things in a way that actually communicates.