@peschkaj has joined the channel
Hey, everyone. It’s good to see chatter in here.
The net2 design docs are easy to read. After the concepts are flushed out and the API starts to form, I should be more helpful.
@peschkaj hi! :wave:
@dedbox which reminds me: Axon is very interesting. I want net2
to allow transports between named agents in different Racket threads, and it might be easier to design that by integrating it with Axon to prove the concept
hello, @notjack
@notjack named agents, as in actors or maybe pi calculus channels?
@dedbox named agents as in “things that can answer authoritatively for a specific URI authority”
@notjack ok, so universally named agents.
yes, so non-axon networks can communicate with axon networks and vice versa
Cool! I see a lot of overlapping concepts between net2 and axon.
agreed
I think I want net2
to be responsible for providing what’s needed for Racket concurrency / distribution abstractions to talk to the wider internet
so, URIs
I’m not planning on implementing any new sort of concurrency paradigm like axon / marketplace / syndicate
but I do want a racket program to be able to run servers implemented in axon / marketplace / syndicate all at once in the same Racket process, and I want non-racket programs to be able to talk to those servers
It’s tempting to keep plugging on code,. Maybe it’s better to document concepts first, to discuss?
I wrote some of this in the ROADMAP.md doc, described as “Racket Transport Addresses” (or something like that, not remembering off the top of my head)
more discussion here gladly welcomed
Ok, so axon gives TCP connectors and listeners, and sexp (and json and HTTP1) messengers.
A core design principle is to hoist ordinary functions to network-bound actor-like filters.
gotcha, looking over the tests I was able to see how it let you take functions and (in my basic, fuzzy understanding) turn them into unix processes with lifetimes that can send and receive their results and arguments
Close! It uses racket threads l.
right, each “process” wraps a thread that can be both gracefully and forcibly killed by other “processes”
and a process can have shutdown / termination logic
and they’re racket green threads, so super cheap and you can have a million of ’em
(I’m assuming you’re typing stuff? no rush but slack keeps changing its mind on whether you are)
Yeah, that’s pretty much the whole of it. Re-reading ROADMAP.md now.
On phone. Slack gets extra confused
gotcha
how familiar are you with http virtual hosting?
I built out a few large HTTP virtual hosting services, before “the cloud.”
perfect
so an explicit feature I want net2
to enable is for a single Racket process with a single Racket thread to offer multiple http APIs to the internet by way of virtual hosting and using racket transport addresses to locate distinct in-memory servers and communicate their locations to arbitrary internet clients
basically I want a single racket program to be able to run multiple http services / proxies / gateways etc, and I want client code in that racket program to interact with those services without having to know that they’re local so that deployment topology is transparent to the client
which means URI trickery
So, you want all agents to appear “remote,” even though some might be local?
yes - or at least, I want the interface to remote and local agents to allow changing an agent from local to remote without requiring that clients of that agent be restarted or redeployed
otherwise, distributed programming will never be simple
oops - in the part above where I said “for a single Racket process with a single Racket thread” I meant “OS thread” in the second part
That’s starting to sound like agent mobility.
but lower level
agent interface mobility?
that sounds reasonable
the broader problem I’m trying to solve here is making it easier to do things in heterogeneous distributed systems, rather than homogenous ones
You wrote that chains of ports would imact performance. So, routing occurs at the bytes level?
yes, because in a heterogenous distributed system messages are a lie
there are only bytes
go tit
*got it
in net2
, communication between authorities will always occur over transports and transports only send and receive bytes
to send and receive messages, you have to build messaging protocols on top of byte transports and that is a userland responsibility, not something defined by some common runtime that all parties in the network share
because requiring that all agents in the system share a common runtime or a common definition of what bytes are what means you can’t horizontally scale, and it means you can’t communicate at all with agents outside the system that don’t share that runtime - so you end up with two notions of networking, one for “actors inside my collection of distributed erlang nodes” and one for “ugh this is how I do tcp connections to everybody else”
That’s what turned me away from ZeroMQ
The “lower” half of axon
tries to solve that problem.
which bits are the lower half?
the encoder, for example
it ties a printer to an output-port
then it prints the messages it receives onto the port
I guess by “lower” I meant closer to actual byte IO
gotcha
yes, that part I imagine net2
being responsible for
(in an ideal world, Hackett would be in a position where there’d be ways to easily derive strongly typed, performant byte serializations and deserializations for message types and net2
users would just use that)
That’s good for axon
.
It’s supposed to be for concurrent messaging over the network.
All of its major bugs have involved closing ports
oh! that’s good to know, because it means choosing to integrate the disposable
package directly with net2
is a good idea
net2
connectors will (hopefully) manage all connection pooling and connection lifecycles
That’s the only feature from python asyncio I miss in Racket
I’ve heard of python asyncio but haven’t used it, how does it handle that?
It just makes the sockets you open play nicely with the cooperative event loop.
But it gives you a way to group sockets together and time events precisely
Racket is more of a thread soup.
End up using more semaphores.
interesting
found this part of docs: https://docs.python.org/3/library/asyncio-eventloop.html#creating-connections
heh, they even call abstract bidirectional streams “transports”
I thought so too
I mean, I thought of that doc while reading the roadmap
yeah there’s a lot of similar ideas, which is good to see
I think the main differentiator net2
will focus on is making host/port/protocol parameters suitably abstract, so we can do weird things like hook up Axon networks to the internet
also connection management responsibilities will be divided between disposables and racket custodians and explicitly controllable by those means, instead of just being something magic that the framework does for you
That would be great. I’m writing a multiplayer game server right now. The first version was in python using asyncio, as an exercise. It was not so pleasant.
ah, games will be tricky
since they often use udp and datagram-based network logic
net2
is explicitly not touching that kind of I/O, at least for the near future
That was conspicuously missing from the roadmap. Can you elaborate?
sure
the gist of it is: that level of I/O is too low for me to get anything useful done if I try to integrate it into racket
net2
will assume reliable bidirectional bytestreams exist, and build a pile of stuff on top of that
however!
if a different library implemented ways to define reliable bidi bytestream protocols on top of datagram based sockets, possibly in ways that let programs assume things you can’t assume in TCP so you can get better perf, then that library could use net2
to make transports that the rest of net2
could work on top of
and if that doesn’t work, it ought to be possible to adapt net2
codecs and messengers to work with datagram communication without serious effort
so there’s parts of net2
that could in theory work with that sort of communication, but making it a design constraint means I’d have to do a lot more work before I could make interesting things with http on top of net2
many multiplayer games that don’t have very strict latency requirements can often be made to work over TCP though, so it’s not a total loss
also, the Tokio framework in Rust is breaking a lot of new ground in this area already so I’d like to give them more time to figure these design problems out so I don’t have to :p
tonyg has done a bunch of stuff in that space too (have you seen his library for talking to ethernet directly in racket? so cool)
I thought it might fit nicely. Generalize port+address to URI. Provide encoders to chunk or truncate messages over ~64K. Do not provide UDP transport. That takes care of at least 80% of UDP use cases and keeps me in the library.
it’s definitely possible, and likely possible to do quite nicely
just not gonna spend time thinking about it yet
axon
has code for that right now
the only missing Racket piece to this game server is a user-friendly linear algebra engine
the udp.rkt
module I presume?
oh!
there’s a package for that I think
raco pkg install glpk
hmm, I’m not sure if that’s for general linear algebra though
seems to only be for limited kinds of optimization problems involving linear algebra?
….it’s been a very long time since I took classes on this stuff
Cool, thanks.
@dedbox btw I peeked at your game server, specifically the sqlite querying parts. I think @royall is considering how to make sql nicer in Racket so you two might have stuff to talk about there.
Here’s the work in progress: https://github.com/dedbox/axon-game-server/blob/snapshot/server.rkt
It shows TCP and UDP in action together.
At top, it listens for UDP broadcast pings for local server discovery.
Then, it uses TCP for asset data exchange.
Finally, it uses UDP again to blast world state updates l.
This hybrid approach is convenient, reliable, and performant in the right places, and the API for both protocols is similar.
what performance are you targeting? in terms of latency between clients seeing state changes
The SQL is a necessary evil at this point. I wouldn’t mind a friendlier persistence API.
Low latency for world updates, low complexity for asset exchange and session management.
low latency as in less than a second, less than a few frames in 60fps, or less than a millisecond? ballpark guesses are fine
Single digits milliseconds.
okay, that’s entirely doable with TCP and nontrivial messages
depending on ping latency
Sure. This hybrid pattern represents a more general use case, for things like VoIP and RTSP.
gotcha
Cool, so now I can imagine how axon
would use net2
. Maybe I can sketch some example uses.
I’ll need to figure out and document how racket transport addresses work first, which I’ll do after I get connectors, listeners, and TCP/TLS/Unix transports implemented
I’m thinking specifically of some database conveniences along the lines of Clojure’s HugSQL. Probably a good excuse to build myself a #lang
and include some conveniences like optionally using transactions for bulk writes or returning large result sets via generator.
partial markdown be damned
tbqh I don’t understand half of today’s conversation, so this will all be a nice learning experience for me
I want to finish my current project of building a knockoff of the Symfony CLI component before starting something else in earnest
It’s not a good language, but PHP’s widespread adoption means there’s lots of handy libraries for it. I’d like to see Racket have the same level of off-the-shelf convenience for common tasks.
@royall the practical upshot of the stuff talked about is that if you’ve got some racket code that needs to talk to some http API, and that API does something weird, your racket code can easily start a proxy server between you and that API and that proxy can translate the weird API into a much cleaner API. So it’s easy to make reusable standards-based HTTP client libraries that do really powerful and neat things, but you still use them with actual real-life non-standards-based HTTP services.
When you put it that way, it sounds really superb
like, HTTP auth schemes are really incredible and can be dynamically discovered and reconfigured on the fly. But fucking nobody uses them (pardon my french). So a client library that does those fancy things is useless unless it’s easy to translate between existing services like AWS or whatever the startup kids are using these days.
@royall hello!
@notjack will net2
support half-open TCP connections?
@dedbox I would like it to, though at the moment I’m relying on the kernel tcp / ip stack which doesn’t grant as much control over that
I had trouble making the client side see when the serving side closes its out-port.
when the client was already waiting to receive
Do you know if that’s expected TCP or Racket behavior?
It depends on whether the connection was closed gracefully, forcibly, or just abandoned
close-output-port
That’s a graceful close. The kernel will send a TCP FIN packet which should cause the kernel on the other end to close the connection, then in racket land you’ll get an eof?
value when you try to read from the port
Assuming both sides of connection are implemented in racket?
yes
Forceful close is sending TCP RST packet, abandoned is one side just stops sending anything at all (like if network connectivity dropped) and the other side closes when it can’t receive ACKs on either sent data or heartbeat packets
With the racket/tcp
module, there is no way for racket code to send anything other than a FIN packet
Only kernel is capable of RST or abandoning
Ok, good to know.