Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cloud messages for Load81 #32

Open
antirez opened this issue Mar 10, 2012 · 5 comments
Open

Cloud messages for Load81 #32

antirez opened this issue Mar 10, 2012 · 5 comments

Comments

@antirez
Copy link
Owner

antirez commented Mar 10, 2012

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.

@agladysh
Copy link

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.

@antirez
Copy link
Owner Author

antirez commented Mar 10, 2012

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.

@agladysh
Copy link

API key + a service to get it via e-mail?

@seclorum
Copy link

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 ..

@cobrajs
Copy link

cobrajs commented Mar 13, 2012

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()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants