Skip to content

Commit 906607f

Browse files
committed
splitted README into proper doc generated with mkdocs
1 parent a2d4a53 commit 906607f

21 files changed

Lines changed: 323 additions & 292 deletions

README.md

Lines changed: 7 additions & 291 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
MRQ
2-
===
1+
# MRQ
32

43
Mongo Redis Queue - A distributed worker task queue in Python
54

5+
Full documentation is available on [readthedocs]()
6+
67
/!\ MRQ is not yet ready for public use. Soon!
78

8-
Why?
9-
====
9+
# Why?
1010

1111
MRQ is an opinionated task queue. It aims to be simple and beautiful like http://python-rq.org while having performance close to http://celeryproject.org
1212

@@ -29,299 +29,15 @@ The main features of MRQ are:
2929
* **Integrated memory leak debugger:** Track down jobs leaking memory and find the leaks with objgraph.
3030

3131

32-
33-
Dashboard
34-
=========
35-
36-
A strong focus was put on the tools and particularly the dashboard. After all it is what you will work with most of the time!
32+
# Dashboard Screnshots
3733

3834
![Job view](http://i.imgur.com/xaXmrvX.png)
3935

4036
![Worker view](http://i.imgur.com/yYUMCbm.png)
4137

42-
There are too much features on the dashboard to list, but the goal is to have complete visibility and control over what your workers are doing!
43-
44-
45-
Design
46-
======
47-
48-
A talk+slides about MRQ's design is upcoming.
49-
50-
A couple things to know:
51-
- We use Redis as a main queue for task IDs
52-
- We store metadata on the tasks in MongoDB so they can be browsable and managed more easily.
53-
54-
55-
56-
Performance
57-
===========
58-
59-
On a MacbookPro, we see 1300 jobs/second in a single worker process with very simple jobs that store results, to measure the overhead of MRQ. However what we are really measuring there is MongoDB's write performance.
60-
61-
62-
Install & dependencies
63-
======================
64-
65-
MRQ can be installed via PIP:
66-
67-
```pip install mrq```
68-
69-
MRQ has only been tested with Python 2.7+. External service dependencies are MongoDB >= 2.4 and Redis >= 2.6 (we use LUA scripting to boost performance and provide extra safety).
70-
71-
You will need [Docker](http://docker.io) to run our unit tests. Our [Dockerfile](https://github.com/pricingassistant/mrq/blob/master/Dockerfile) is actually a good way to see a complete list of dependencies, including dev tools like graphviz for memleak images.
72-
73-
You may want to convert your logs db to a capped collection : ie. run db.runCommand({"convertToCapped": "mrq_jobs", "size": 10737418240})
74-
75-
76-
Configuration
77-
=============
78-
79-
Check all the [available config options](mrq/config.py)
80-
81-
For each of these values, configuration is loaded in this order by default:
82-
- Command-line arguments (`mrq-worker --redis=redis://127.0.0.1:6379`)
83-
- Environment variables prefixed by MRQ_ (`MRQ_REDIS=redis://127.0.0.1:6379 mrq-worker`)
84-
- Python variables in a config file, by default `mrq-config.py` (`REDIS="redis://127.0.0.1:6379"` in this file)
85-
86-
Most of the time, you want to set all your configuration in a `mrq-config.py` file in the directory where you will launch your workers, and override some of it from the command line.
87-
88-
On Heroku, environment variables are very handy because they can be set like `heroku config:set MRQ_REDIS=redis://127.0.0.1:6379`
89-
90-
91-
Command-line use
92-
================
93-
94-
All the command-line tools support a set of common configuration flags, defined in [config.py](https://github.com/pricingassistant/mrq/blob/master/mrq/config.py). Use --help with any of them to see the full list.
95-
96-
- `mrq-worker` starts a worker
97-
- `mrq-dashboard` starts the web dashboard on the default port
98-
- `mrq-run` runs a task. If you add the `--async` option that will enqueue it to be later ran by a worker
99-
100-
Typical usage is:
101-
```
102-
$ mrq-run tasks.mylib.myfile.MyTask '{"param1": 1, "param2": True}'
103-
```
104-
105-
106-
Job maintenance
107-
===============
108-
109-
MRQ can provide strong guarantees that no job will be lost in the middle of a worker restart, database disconnect, etc...
110-
111-
To do that, you should add these recurring scheduled jobs to your mrq-config.py:
112-
113-
```
114-
SCHEDULER_TASKS = [
115-
116-
# This will requeue jobs marked as interrupted, for instance when a worker received SIGTERM
117-
{
118-
"path": "mrq.basetasks.cleaning.RequeueInterruptedJobs",
119-
"params": {},
120-
"interval": 5 * 60
121-
},
122-
123-
# This will requeue jobs marked as started for a long time (more than their own timeout)
124-
# They can exist if a worker was killed with SIGKILL and not given any time to mark
125-
# its current jobs as interrupted.
126-
{
127-
"path": "mrq.basetasks.cleaning.RequeueStartedJobs",
128-
"params": {},
129-
"interval": 3600
130-
},
131-
132-
# This will requeue jobs 'lost' between redis.blpop() and mongo.update(status=started).
133-
# This can happen only when the worker is killed brutally in the middle of dequeue_jobs()
134-
{
135-
"path": "mrq.basetasks.cleaning.RequeueLostJobs",
136-
"params": {},
137-
"interval": 24 * 3600
138-
}
139-
]
140-
```
141-
142-
Obviously this implies that all your jobs should be *idempotent*, meaning that they could be done multiple times, maybe partially, without breaking your app. This is a very good design to enforce for your whole task queue, though you can still manage locks yourself in your code that make sure a block of code will only run once.
143-
144-
145-
Raw queues
146-
==========
147-
148-
With regular queues, MRQ stores the task metadata in MongoDB and the task IDs in a Redis list. This design allows a good compromise between performance and visibility.
149-
150-
Raw queues give you more performance and some new features in exchange for a bit less visibility. In their case, only the parameters of a task are stored in serialized form in Redis when queued, and they are inserted in MongoDB only after being dequeued by a worker.
151-
152-
There are 4 types of raw queues. The type of a queue is determined by a suffix in its name:
153-
154-
* ```_raw``` : The simpliest raw queue, stored in a Redis LIST.
155-
* ```_set``` : Stored in a Redis SET. Gives you the ability to have "unique" tasks: only one (task, parameters) couple can be queued at a time.
156-
* ```_sorted_set``` : The most powerful MRQ queue type, stored in a Redis ZSET. Allows you to order (and re-order) the tasks to be dequeued. Like ```_set```, task parameters will be unique.
157-
* ```_timed_set``` : A special case of ```_sorted_set```, where tasks are sorted with a UNIX timestamp. This means you can schedule tasks to be executed at a precise time in the future.
158-
159-
Raw queues need a special entry in the configuration to unserialize their "raw" parameters in a regular dict of parameters. They also need to be linked to a regular queue ("default" if none) for providing visibility and retries, after they are dequeued from the raw queue.
160-
161-
This is an example of raw queue configuration:
162-
163-
```python
164-
RAW_QUEUES = {
165-
"myqueue_raw": {
166-
"job_factory": lambda rawparam: {
167-
"path": "tests.tasks.general.Add",
168-
"params": {
169-
"a": int(rawparam.split(" ")[0]),
170-
"b": int(rawparam.split(" ")[1])
171-
}
172-
},
173-
"retry_queue": "high"
174-
}
175-
}
176-
```
177-
178-
This task adds two integers. To queue tasks, you can do from the code:
179-
180-
```python
181-
from mrq.queue import send_raw_tasks
182-
183-
send_raw_tasks("myqueue_raw", [
184-
["1 1"],
185-
["42 8"]
186-
])
187-
```
188-
189-
To run them, start a worker listening to both queues:
190-
191-
```
192-
$ mrq-worker high myqueue_raw
193-
```
194-
195-
Queueing on timed sets is a bit different, you can pass unix timestamps directly:
196-
197-
```python
198-
from mrq.queue import send_raw_tasks
199-
import time
200-
201-
send_raw_tasks("myqueue_timed_set", {
202-
"rawparam_xxx": time.time(),
203-
"rawparam_yyy": time.time() + 3600 # Do this in an hour
204-
})
205-
```
206-
207-
For more examples of raw queue configuration, check https://github.com/pricingassistant/mrq/blob/master/tests/fixtures/config-raw1.py
208-
209-
210-
Hunting memory leaks
211-
====================
212-
213-
Memory leaks can be a big issue with gevent workers because several tasks share the same python process.
214-
215-
Thankfully, MRQ provides tools to track down such issues. Memory usage of each worker is graphed in the dashboard and makes it easy to see if memory leaks are happening.
216-
217-
When a worker has a steadily growing memory usage, here are the steps to find the leak:
218-
219-
* Check which jobs are running on this worker and try to isolate which of them is leaking and on which queue
220-
* Start a dedicated worker with ```--trace_memory --gevent 1``` on the same queue : This will start a worker doing one job at a time with memory profiling enabled. After each job you should see a report of leaked object types.
221-
* Find the most unique type in the list (usually not 'list' or 'dict') and restart the worker with ```--trace_memory --gevent 1 --trace_memory_type=XXX --trace_memory_output_dir=memdbg``` (after creating the directory memdbg).
222-
* There you will find a graph for each task generated by [objgraph](https://mg.pov.lt/objgraph/) which is incredibly helpful to track down the leak.
223-
224-
225-
Simulating network latency
226-
==========================
227-
228-
Sometimes it is helpful in local development to simulate an environment with higher network latency.
229-
230-
To do this we added a ```--add_network_latency=0.1``` config option that will add (in this case) a random delay between 0 and 0.1 seconds to every network call.
231-
232-
233-
Worker concurrency
234-
==================
235-
236-
The default is to run tasks one at a time. You should obviously change this behaviour to use Gevent's full capabilities with something like:
237-
238-
`mrq-worker --processes 3 --gevent 10`
239-
240-
This will start 30 greenlets over 3 UNIX processes. Each of them will run 10 jobs at the same time.
241-
242-
As soon as you use the `--processes` option (even with `--processes=1`) then supervisord will be used to control the processes. It is quite useful to manage long-running instances.
243-
244-
On Heroku's 512M dynos, we have found that for IO-bound jobs, `--processes 4 --gevent 30` may be a good setting.
245-
246-
247-
Metrics & Graphite
248-
==================
249-
250-
MRQ doesn't support sending metrics to Graphite out of the box but makes it extremely easy to do so.
251-
252-
All you have to do is add this hook in your mrq-config file:
253-
254-
```
255-
256-
# Install this via pip
257-
import graphiteudp
258-
259-
# Initialize the Graphite UDP Client
260-
_graphite_client = graphiteudp.GraphiteUDPClient(host, port, prefix, debug=False)
261-
_graphite_client.init()
262-
263-
def METRIC_HOOK(name, incr=1, **kwargs):
264-
265-
# You can use this to avoid sending too many different metrics
266-
whitelisted_metrics = ["queues.all.", "queues.default.", "jobs."]
267-
268-
if any([name.startswith(m) for m in whitelisted_metrics]):
269-
_graphite_client.send(name, incr)
270-
271-
272-
```
273-
274-
If you have another monitoring system you can plug anything in this hook to connect to it!
275-
276-
277-
Tests
278-
=====
279-
280-
Testing is done inside a Docker container for maximum repeatability.
281-
We don't use Travis-CI or friends because we need to be able to kill our process dependencies (MongoDB, Redis, ...) on demand.
282-
283-
Therefore you need to ([install docker](https://www.docker.io/gettingstarted/#h_installation)) to run the tests.
284-
If you're not on an os that supports natively docker, don't forget to start up your VM and ssh into it.
285-
286-
```
287-
$ make test
288-
```
289-
290-
You can also open a shell inside the docker (just like you would enter in a virtualenv) with:
291-
292-
```
293-
$ make docker (if it wasn't build before)
294-
$ make ssh
295-
```
296-
297-
298-
PyPy
299-
====
300-
301-
Earlier in its development MRQ was tested successfully on PyPy but we are waiting for better PyPy+gevent support to continue working on it, as performance was worse than CPython.
302-
303-
304-
Useful third-party utils
305-
========================
306-
307-
* http://superlance.readthedocs.org/en/latest/
308-
38+
# Get Started
30939

310-
Credits
311-
=======
31240

313-
Inspirations:
314-
* RQ
315-
* Celery
31641

317-
JS libraries used in the Dashboard:
318-
* http://backbonejs.org
319-
* http://underscorejs.org
320-
* http://requirejs.org
321-
* http://momentjs.com
322-
* http://jquery.com
323-
* http://datatables.net
324-
* https://github.com/Jowin/Datatables-Bootstrap3/
325-
* https://github.com/twbs/bootstrap
42+
# More
32643

327-
... as well as all the Python modules in requirements.txt!

docs/cli/index.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
# Commands
3+
4+
All the command-line tools support a set of common configuration flags, defined in [config.py](https://github.com/pricingassistant/mrq/blob/master/mrq/config.py). Use --help with any of them to see the full list.
5+
6+
- `mrq-worker` starts a worker
7+
- `mrq-dashboard` starts the web dashboard on the default port
8+
- `mrq-run` runs a task. If you add the `--async` option that will enqueue it to be later ran by a worker
9+
10+
Typical usage is:
11+
```
12+
$ mrq-run tasks.mylib.myfile.MyTask '{"param1": 1, "param2": True}'
13+
```

docs/cli/mrq-dashboard.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# mrq-dashboard

docs/cli/mrq-run.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# mrq-run

docs/cli/mrq-worker.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# mrq-worker
2+
3+
## Concurrency
4+
5+
The default is to run tasks one at a time. You should obviously change this behaviour to use Gevent's full capabilities with something like:
6+
7+
`mrq-worker --processes 3 --gevent 10`
8+
9+
This will start 30 greenlets over 3 UNIX processes. Each of them will run 10 jobs at the same time.
10+
11+
As soon as you use the `--processes` option (even with `--processes=1`) then supervisord will be used to control the processes. It is quite useful to manage long-running instances.
12+
13+
14+
## Simulating network latency
15+
16+
Sometimes it is helpful in local development to simulate an environment with higher network latency.
17+
18+
To do this we added a ```--add_network_latency=0.1``` config option that will add (in this case) a random delay between 0 and 0.1 seconds to every network call.

docs/configuration.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Configuration
2+
3+
Check all the [available config options](mrq/config.py)
4+
5+
For each of these values, configuration is loaded in this order by default:
6+
- Command-line arguments (`mrq-worker --redis=redis://127.0.0.1:6379`)
7+
- Environment variables prefixed by MRQ_ (`MRQ_REDIS=redis://127.0.0.1:6379 mrq-worker`)
8+
- Python variables in a config file, by default `mrq-config.py` (`REDIS="redis://127.0.0.1:6379"` in this file)
9+
10+
Most of the time, you want to set all your configuration in a `mrq-config.py` file in the directory where you will launch your workers, and override some of it from the command line.
11+
12+
On Heroku, environment variables are very handy because they can be set like `heroku config:set MRQ_REDIS=redis://127.0.0.1:6379`

docs/dashboard.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Dashboard
2+
=========
3+
4+
A strong focus was put on the tools and particularly the dashboard. After all it is what you will work with most of the time!
5+
6+
![Queues view](http://i.imgur.com/H2nsgq2.png)
7+
8+
![Jobs view](http://i.imgur.com/SNsYhuQ.png)
9+
10+
11+
There are too many features on the dashboard to list, but the goal is to have complete visibility and control over what your workers are doing!

0 commit comments

Comments
 (0)