load81
load81 copied to clipboard
Cloud messages for Load81
Load81 programs are currently self-contained into a box without the ability to talk with the external world. Specifically they can't talk with other instances of the same program running in other Load81 instances around the world! What a shame. So what I'm proposing and implementing is a Cloud message service for Load81.
API
A Load81 program listen to channels using the listen() function. Multiple calls to listen are allowed in order to listen to multiple channels, and it is also possible to no longer listen to a given channel using unlisten(). Example:
listen("asteroids")
listen("foo","bar")
unlisten("asteroids")
Every time a message is received the function receive is called. It is important to understand how this works. The normal flow of a Load81 program is to call setup the first time, and then call draw. When the program is listening to one or more channels, the function receive is called before the function draw is called, and is called for all the pending messages currently in the queue, so for instance you may have 5 calls to receive, and finally the call to draw, and so forth.
This is how the function receive is defined in a program:
function receive(sender,channel,msg)
....
end
The three arguments are:
- sender: a 40 chars hex string that uniquely identifies the sender instance (this instance ID changes at every run).
- channel: the channel that received the message.
- msg: a Lua object (number, string, table, boolean, ...)
You don't receive messages that are sent by your instance.
In order to send messages you simply use:
send("channel",lua_object)
You can send messages to channels you are not listening without problems if you wish.
Implementation
The implementation using a Redis instance running at pubsub.load81.com, with only PUBLISH and SUBSCRIBE commands enabled. The instance is configured to drop clients that reached an output buffer of a few hundred kbytes so that it is not possible to abuse the instance just listening to channels without actually reading data.
Every Load81 instance creates a random identifier at startup, either reading from /dev/urandom or using a mix of time and rand() if urandom is not available.
Messages are send to Redis as <Instance ID>:<MessagePack representation of the Lua object sent>
.
Messages are both sent and received using a non blocking socket. At every iteration we read what's new in the socket, and call a function that will put all the entire messages in the queue, parsing the Redis protocol.
From the point of view of sending, we'll try to send messages every time the user calls the send() function in order to minimize latency, but if there is no room on the kernel socket buffer we'll bufferize the write and send it when possible (at the next iteration of the event processor, that is, by default, 30 times per second).
Abuses
From the outside attacker this is a free message hub, but I think it's fine since for instance an IRC server has the same problem.... and I think no one will try to abuse this stuff, at least I hop.
From the outside attacker this is a free message hub, but I think it's fine since for instance an IRC server has the same problem.... and I think no one will try to abuse this stuff, at least I hop.
IRC is easier, of course (and is actively abused).
But, I believe, that you should plan for this anyway. This is a perfect way for some random offended person to shut down pubsub.load81.com — since you'll have no means to ban abusive clients:
- Use pubsub for some bad things.
- Write to abuse of your data center.
- Data center tells you to stop.
- You can't.
- ...
- PROFIT, you're banned.
Yes... but I don't really thing there is an alternative if we want to maintain such a simple API and low barrier to entry... to the messaging party :) We'll try let's see what happens.
API key + a service to get it via e-mail?
I like this a lot, except for one thing:
When the program is listening to one or more channels, the function receive is called before the function draw is called, and is called for all the pending messages currently in the queue, so for instance you may have 5 calls to receive, and finally the call to draw, and so forth.
This has the potential that I could post messages to a channel and fill everyones receive() queue, degrading performance immensely before a draw() is issued - 'hanging' client programs and pissing the game-players off. Would it not be better to make receive()/draw() synchronous? I'm not sure I'm thinking about this properly, but it seems to me that having the requirement be that the queue be emptied between draw()'s would degrade FPS somehow - not good for games, even the simple variety.
Then again, maybe another option would be to have two kinds of message types: "replace-upon-insert", and "stack", although maybe this is also just asking for trouble ..
Maybe have receive() run before draw(), and it processes as many messages as it can before the next draw() call, so that it wouldn't degrade FPS but it could handle the messages when the load is lower?
Something like: do recieve() until drawNeeded draw()