5 ms·
Disque is definitely exciting, and looks like it can replace RabbitMQ, which has serious flaws in its clustering design. I'm looking forward to trying it out.
by lobster_johnson 11y ago
Disque is definitely exciting, and looks like it can replace RabbitMQ, which has serious flaws in its clustering design. I'm looking forward to trying it out.
However, if some constructive criticism is permitted, I have to say that, having written distributed applications for many years, I have come to dislike the "classical" push/pop queue data model:
* Acking is a bad idea. It requires the broker to manage a lot of state, including locking and timeouts.
* Re-queuing invalidates total ordering.
* On the performance side, parallel distributed queue consumption (which also breaks total ordering) is directly at odds with this model.
* Queues as opaque objects — you can only inspect by popping the top message, and you cannot access older, dequeued messages. Fortunately, Disque allows you to read the entire queue without mutating it, but it doesn't look like you can read old messages.
* Complicated queue topologies (fanouts, dead letter queues, etc.) become a logical necessity of the strict FIFO structure. (These topologies need to be declared every time the client starts up, and introduces the possibility of schema conflicts.)
* Logical de-duping is probably not possible.
Apache Kafka gets the data model right. It wisely acknowledges that queues are linear and should stay that way: In Kafka, queues are strictly append-only logs where every consumer has a cursor to the last position it read. In this model, many of the classical concerns melt away: Acks/nacks are unnecessary (consumers simply "commit" their position); total ordering is always preserved (since the queue cannot be reordered) and parallelism is made explicit (through named partitions); de-duping is trivial, and complicated topologies are largely eliminated (AMQP-type "exchanges" that fan out to separate queues are unnecessary because multiple readers can all consume the same queue without changing it, as their position is independent of the queue); and you get to choose either at-most-once or at-least-once delivery consistency by how carefully you manage your offset.
Since logs are strictly linear, you are also given the choice of how much history to keep — all of it, if you want — which opens up some interesting use cases that are not possible with classical brokers.
Kafka isn't perfect. It's a huge pain if you're not in Java land. There are no modern, mature "high-level" client implementations for Go, Ruby or Node.js. Its reliance on the JVM and on ZooKeeper makes it fairly heavyweight, both on the server and on the consumer side (the new API for broker-stored offsets simplifies things, but non-Java clients are far behind). I would really love to have a lighter-weight, language-agnostic Kafka-like implementation without the Java baggage.
Last point: The fact that Disque calls its messages "jobs" makes me a little disappointed that it is, in fact, not a job management system. I'd love a solid, distributed job manager.
- Jasper_ 11y agoKafka has a different data model which works for some scenarios, but not others. We attempted to use Kafka as part of a job management system, where long-running jobs were scheduled and workers consumed partitions, but what we found was that since consumers work on partitions, a long-running task could block an entire partition's worth of work, with no way to migrate it to another partition. Kafka works really well when the bottleneck is the broker to begin with -- if you have a lot of small, lightweight messages being passed to other systems, and the broker is having trouble keeping up. Analytics and logs are great examples where Kafka's data model works really well. But for a smaller number of larger messages that takes seconds to minutes to process (we were using it to schedule data downloads and video processing), workers could be sitting idle while there was still plenty more work to do. We moved to Amazon SQS for now, but I imagine we'll move to something like Disque in the future.
- lobster_johnson 11y agoI would argue that job management is unrelated to messaging, at least according my loose definition of a task that: * Has a well-defined lifetime — unstarted, running, paused, completed successfully, or failed; * Is executed from some kind of parameterized "job specification" that describes its inputs and desired behavior; * Has state data (e.g., completion % progress, log output, metrics, transactional continutation state for pausing/retrying); * Can be scheduled on multiple nodes; * Can be created and scheduled multiple times; * Can be scheduled by priority; * Can have its resources (RAM, CPU, I/O) constrained. The thing is, queues are terrible at this. People continue to abuse messaging systems as a scheduler, and it always sucks. For example, consider acking: For a long-running tasks that might take hours, do you really want to hold up the broker's internal lock for all that time? What if you restart the broker — the task is still running, now you decoupled its running state from the queue. Similarly, queues are terrible at finding out what's waiting, or what has happened. By using the queue as job state, you are coupling scheduling with execution, which is just wrong. A queue (one that supports priorities, mind you) is, however, excellent for management commands: "Start job X", "pause job X" and so on. But you will want to maintain execution state separately from the queue. A relational database is quite good at this, though one could use a NoSQL databases, too. First, create a row representing the job: {id="job1", state="new", type="import_stuff", params="..."}. Then push a queue message {commmand="start_job", job_id="job1"}. Run a queue consumer or ten on each node. Each consumer acks the message, finds the job in the table and starts it. One could run it as a child process, or in a Docker container. One could of course use an orchestration service (like Mesos/Marathon) to run it, and monitor the service for container events and update the job table appropriately. I would want to take it further and allow the job to update itself through API, so that it could publish metrics and log messages, as well as maintain whatever data is useful snapshotting. For example, a long-running task to import data into a database would benefit from maintaining a cursor so that you could pause/kill the job and later resume it at the point where it was.