Browse Source

Removes clickcounter tutorial

Ask Solem 13 years ago
parent
commit
6a4f6e10a8

+ 11 - 12
docs/faq.rst

@@ -657,28 +657,27 @@ How can I run a task once another task has finished?
 ----------------------------------------------------
 ----------------------------------------------------
 
 
 **Answer**: You can safely launch a task inside a task.
 **Answer**: You can safely launch a task inside a task.
-Also, a common pattern is to use callback tasks:
+Also, a common pattern is to add callbacks to tasks:
 
 
 .. code-block:: python
 .. code-block:: python
 
 
-    @celery.task()
-    def add(x, y, callback=None):
-        result = x + y
-        if callback:
-            subtask(callback).delay(result)
-        return result
+    from celery.utils.log import get_task_logger
+
+    logger = get_task_logger(__name__)
 
 
+    @celery.task()
+    def add(x, y):
+        return x + y
 
 
     @celery.task(ignore_result=True)
     @celery.task(ignore_result=True)
-    def log_result(result, **kwargs):
-        logger = log_result.get_logger(**kwargs)
-        logger.info("log_result got: %s" % (result, ))
+    def log_result(result):
+        logger.info("log_result got: %r" % (result, ))
 
 
 Invocation::
 Invocation::
 
 
-    >>> add.delay(2, 2, callback=log_result.subtask())
+    >>> (add.s(2, 2) | log_result.s()).delay()
 
 
-See :doc:`userguide/tasksets` for more information.
+See :doc:`userguide/canvas` for more information.
 
 
 .. _faq-cancel-task:
 .. _faq-cancel-task:
 
 

+ 3 - 3
docs/getting-started/next-steps.rst

@@ -141,10 +141,10 @@ These options are described in more detailed in the :ref:`Workers Guide <guide-w
         $ celery --app=proj.celery:celery
         $ celery --app=proj.celery:celery
 
 
 
 
-.. _designing-work-flows:
+.. _designing-workflows:
 
 
-Designing Work-flows
-====================
+*Canvas*: Designing Workflows
+=============================
 
 
 A :func:`~celery.subtask` wraps the signature of a single task invocation:
 A :func:`~celery.subtask` wraps the signature of a single task invocation:
 arguments, keyword arguments and execution options.
 arguments, keyword arguments and execution options.

+ 1 - 1
docs/internals/guide.rst

@@ -64,7 +64,7 @@ Naming
         Sometimes it makes sense to have a class mask as a function,
         Sometimes it makes sense to have a class mask as a function,
         and there is precedence for this in the stdlib (e.g.
         and there is precedence for this in the stdlib (e.g.
         :class:`~contextlib.contextmanager`).  Celery examples include
         :class:`~contextlib.contextmanager`).  Celery examples include
-        :class:`~celery.task.sets.subtask`, :class:`~celery.task.chords.chord`,
+        :class:`~celery.subtask`, :class:`~celery.chord`,
         ``inspect``, :class:`~kombu.utils.functional.promise` and more..
         ``inspect``, :class:`~kombu.utils.functional.promise` and more..
 
 
 - Factory functions and methods must be `CamelCase` (excluding verbs):
 - Factory functions and methods must be `CamelCase` (excluding verbs):

+ 1 - 1
docs/reference/celery.task.base.rst

@@ -1,5 +1,5 @@
 ===================================
 ===================================
- celery.task.base
+ celery.task.base (Deprecated)
 ===================================
 ===================================
 
 
 .. contents::
 .. contents::

+ 0 - 11
docs/reference/celery.task.sets.rst

@@ -1,11 +0,0 @@
-======================================================
- celery.task.sets
-======================================================
-
-.. contents::
-    :local:
-.. currentmodule:: celery.task.sets
-
-.. automodule:: celery.task.sets
-    :members:
-    :undoc-members:

+ 0 - 1
docs/reference/index.rst

@@ -20,7 +20,6 @@
     celery.app.utils
     celery.app.utils
     celery.task
     celery.task
     celery.task.base
     celery.task.base
-    celery.task.sets
     celery.result
     celery.result
     celery.task.http
     celery.task.http
     celery.schedules
     celery.schedules

+ 0 - 242
docs/tutorials/clickcounter.rst

@@ -1,242 +0,0 @@
-.. _tut-clickcounter:
-
-============================================================
- Tutorial: Creating a click counter using Kombu and celery
-============================================================
-
-.. contents::
-    :local:
-
-Introduction
-============
-
-A click counter should be easy, right? Just a simple view that increments
-a click in the DB and forwards you to the real destination.
-
-This would work well for most sites, but when traffic starts to increase,
-you are likely to bump into problems. One database write for every click is
-not good if you have millions of clicks a day.
-
-So what can you do? In this tutorial we will send the individual clicks as
-messages using `kombu`, and then process them later with a Celery periodic
-task.
-
-Celery and Kombu are excellent in tandem, and while this might not be
-the perfect example, you'll at least see one example how of they can be used
-to solve a task.
-
-The model
-=========
-
-The model is simple, `Click` has the URL as primary key and a number of
-clicks for that URL. Its manager, `ClickManager` implements the
-`increment_clicks` method, which takes a URL and by how much to increment
-its count by.
-
-
-*clickmuncher/models.py*:
-
-.. code-block:: python
-
-    from django.db import models
-    from django.utils.translation import ugettext_lazy as _
-
-
-    class ClickManager(models.Manager):
-
-        def increment_clicks(self, for_url, increment_by=1):
-            """Increment the click count for an URL.
-
-                >>> Click.objects.increment_clicks("http://google.com", 10)
-
-            """
-            click, created = self.get_or_create(url=for_url,
-                                    defaults={"click_count": increment_by})
-            if not created:
-                click.click_count += increment_by
-                click.save()
-
-            return click.click_count
-
-
-    class Click(models.Model):
-        url = models.URLField(_(u"URL"), verify_exists=False, unique=True)
-        click_count = models.PositiveIntegerField(_(u"click_count"),
-                                                  default=0)
-
-        objects = ClickManager()
-
-        class Meta:
-            verbose_name = _(u"URL clicks")
-            verbose_name_plural = _(u"URL clicks")
-
-Using Kombu to send clicks as messages
-========================================
-
-The model is normal django stuff, nothing new there. But now we get on to
-the messaging. It has been a tradition for me to put the projects messaging
-related code in its own `messaging.py` module, and I will continue to do so
-here so maybe you can adopt this practice. In this module we have two
-functions:
-
-* `send_increment_clicks`
-
-  This function sends a simple message to the broker. The message body only
-  contains the URL we want to increment as plain-text, so the exchange and
-  routing key play a role here. We use an exchange called `clicks`, with a
-  routing key of `increment_click`, so any consumer binding a queue to
-  this exchange using this routing key will receive these messages.
-
-* `process_clicks`
-
-  This function processes all currently gathered clicks sent using
-  `send_increment_clicks`. Instead of issuing one database query for every
-  click it processes all of the messages first, calculates the new click count
-  and issues one update per URL. A message that has been received will not be
-  deleted from the broker until it has been acknowledged by the receiver, so
-  if the receiver dies in the middle of processing the message, it will be
-  re-sent at a later point in time. This guarantees delivery and we respect
-  this feature here by not acknowledging the message until the clicks has
-  actually been written to disk.
-
-  .. note::
-
-    This could probably be optimized further with
-    some hand-written SQL, but it will do for now. Let's say it's an exercise
-    left for the picky reader, albeit a discouraged one if you can survive
-    without doing it.
-
-On to the code...
-
-*clickmuncher/messaging.py*:
-
-.. code-block:: python
-
-    from celery.messaging import establish_connection
-    from kombu.compat import Publisher, Consumer
-    from clickmuncher.models import Click
-
-
-    def send_increment_clicks(for_url):
-        """Send a message for incrementing the click count for an URL."""
-        connection = establish_connection()
-        publisher = Publisher(connection=connection,
-                              exchange="clicks",
-                              routing_key="increment_click",
-                              exchange_type="direct")
-
-        publisher.send(for_url)
-
-        publisher.close()
-        connection.close()
-
-
-    def process_clicks():
-        """Process all currently gathered clicks by saving them to the
-        database."""
-        connection = establish_connection()
-        consumer = Consumer(connection=connection,
-                            queue="clicks",
-                            exchange="clicks",
-                            routing_key="increment_click",
-                            exchange_type="direct")
-
-        # First process the messages: save the number of clicks
-        # for every URL.
-        clicks_for_url = {}
-        messages_for_url = {}
-        for message in consumer.iterqueue():
-            url = message.body
-            clicks_for_url[url] = clicks_for_url.get(url, 0) + 1
-            # We also need to keep the message objects so we can ack the
-            # messages as processed when we are finished with them.
-            if url in messages_for_url:
-                messages_for_url[url].append(message)
-            else:
-                messages_for_url[url] = [message]
-
-        # Then increment the clicks in the database so we only need
-        # one UPDATE/INSERT for each URL.
-        for url, click_count in clicks_for_urls.items():
-            Click.objects.increment_clicks(url, click_count)
-            # Now that the clicks has been registered for this URL we can
-            # acknowledge the messages
-            [message.ack() for message in messages_for_url[url]]
-
-        consumer.close()
-        connection.close()
-
-
-View and URLs
-=============
-
-This is also simple stuff, don't think I have to explain this code to you.
-The interface is as follows, if you have a link to http://google.com you
-would want to count the clicks for, you replace the URL with:
-
-    http://mysite/clickmuncher/count/?u=http://google.com
-
-and the `count` view will send off an increment message and forward you to
-that site.
-
-*clickmuncher/views.py*:
-
-.. code-block:: python
-
-    from django.http import HttpResponseRedirect
-    from clickmuncher.messaging import send_increment_clicks
-
-
-    def count(request):
-        url = request.GET["u"]
-        send_increment_clicks(url)
-        return HttpResponseRedirect(url)
-
-
-*clickmuncher/urls.py*:
-
-.. code-block:: python
-
-    from django.conf.urls.defaults import patterns, url
-    from clickmuncher import views
-
-    urlpatterns = patterns("",
-        url(r'^$', views.count, name="clickmuncher-count"),
-    )
-
-
-Creating the periodic task
-==========================
-
-Processing the clicks every 30 minutes is easy using celery periodic tasks.
-
-*clickmuncher/tasks.py*:
-
-.. code-block:: python
-
-    from celery.task import PeriodicTask
-    from clickmuncher.messaging import process_clicks
-    from datetime import timedelta
-
-
-    class ProcessClicksTask(PeriodicTask):
-        run_every = timedelta(minutes=30)
-
-        def run(self, **kwargs):
-            process_clicks()
-
-We subclass from :class:`celery.task.base.PeriodicTask`, set the `run_every`
-attribute and in the body of the task just call the `process_clicks`
-function we wrote earlier.
-
-
-Finishing
-=========
-
-There are still ways to improve this application. The URLs could be cleaned
-so the URL http://google.com and http://google.com/ is the same. Maybe it's
-even possible to update the click count using a single UPDATE query?
-
-If you have any questions regarding this tutorial, please send a mail to the
-mailing-list or come join us in the #celery IRC channel at Freenode:
-http://celeryq.org/introduction.html#getting-help

+ 0 - 1
docs/tutorials/index.rst

@@ -10,5 +10,4 @@
 
 
     daemonizing
     daemonizing
     debugging
     debugging
-    clickcounter
     task-cookbook
     task-cookbook

+ 15 - 19
docs/userguide/groups.rst → docs/userguide/canvas.rst

@@ -1,22 +1,20 @@
-.. _guide-sets:
-.. _guide-groups:
+.. _guide-canvas:
 
 
-=======================================
- Groups, Chords, Chains and Callbacks
-=======================================
+============================
+ Canvas: Building Workflows
+============================
 
 
 .. contents::
 .. contents::
     :local:
     :local:
 
 
-.. _sets-subtasks:
-.. _groups-subtasks:
+.. _canvas-subtasks:
 
 
 Subtasks
 Subtasks
 ========
 ========
 
 
 .. versionadded:: 2.0
 .. versionadded:: 2.0
 
 
-The :class:`~celery.task.sets.subtask` type is used to wrap the arguments and
+The :class:`~celery.subtask` type is used to wrap the arguments and
 execution options for a single task invocation:
 execution options for a single task invocation:
 
 
 .. code-block:: python
 .. code-block:: python
@@ -31,7 +29,7 @@ For convenience every task also has a shortcut to create subtasks:
 
 
     task.subtask(args, kwargs, options)
     task.subtask(args, kwargs, options)
 
 
-:class:`~celery.task.sets.subtask` is actually a :class:`dict` subclass,
+:class:`~celery.subtask` is actually a :class:`dict` subclass,
 which means it can be serialized with JSON or other encodings that doesn't
 which means it can be serialized with JSON or other encodings that doesn't
 support complex Python objects.
 support complex Python objects.
 
 
@@ -43,8 +41,7 @@ Also it can be regarded as a type, as the following usage works::
 
 
 This makes it excellent as a means to pass callbacks around to tasks.
 This makes it excellent as a means to pass callbacks around to tasks.
 
 
-.. _sets-callbacks:
-.. _groups-callbacks:
+.. _canvas-callbacks:
 
 
 Callbacks
 Callbacks
 ---------
 ---------
@@ -79,17 +76,16 @@ arguments::
 As expected this will first launch one task calculating :math:`2 + 2`, then
 As expected this will first launch one task calculating :math:`2 + 2`, then
 another task calculating :math:`4 + 8`.
 another task calculating :math:`4 + 8`.
 
 
-.. _sets-taskset:
-.. _groups-group:
+.. _canvas-group:
 
 
 Groups
 Groups
 ======
 ======
 
 
-The :class:`~celery.task.sets.group` enables easy invocation of several
+The :class:`~celery.group` enables easy invocation of several
 tasks at once, and is then able to join the results in the same order as the
 tasks at once, and is then able to join the results in the same order as the
 tasks were invoked.
 tasks were invoked.
 
 
-``group`` takes a list of :class:`~celery.task.sets.subtask`'s::
+``group`` takes a list of :class:`~celery.subtask`'s::
 
 
     >>> from celery import group
     >>> from celery import group
     >>> from tasks import add
     >>> from tasks import add
@@ -115,12 +111,12 @@ The first argument can alternatively be an iterator, like::
 
 
     >>> group(add.subtask((i, i)) for i in range(100))
     >>> group(add.subtask((i, i)) for i in range(100))
 
 
-.. _sets-results:
+.. _canvas-group-results:
 
 
-Results
--------
+Group Results
+-------------
 
 
-When a  :class:`~celery.task.sets.group` is applied it returns a
+When a  :class:`~celery.group` is applied it returns a
 :class:`~celery.result.TaskSetResult` object.
 :class:`~celery.result.TaskSetResult` object.
 
 
 :class:`~celery.result.TaskSetResult` takes a list of
 :class:`~celery.result.TaskSetResult` takes a list of

+ 1 - 1
docs/userguide/index.rst

@@ -15,7 +15,7 @@
     calling
     calling
     workers
     workers
     periodic-tasks
     periodic-tasks
-    groups
+    canvas
     remote-tasks
     remote-tasks
     routing
     routing
     monitoring
     monitoring

+ 1 - 1
docs/userguide/overview.rst

@@ -32,7 +32,7 @@ a "task".
 
 
 * Go to :ref:`guide-tasks`.
 * Go to :ref:`guide-tasks`.
 * Go to :ref:`guide-calling`.
 * Go to :ref:`guide-calling`.
-* Go to :ref:`guide-sets`
+* Go to :ref:`guide-canvas`
 * Go to :ref:`guide-beat`.
 * Go to :ref:`guide-beat`.
 * Go to :ref:`guide-webhooks`.
 * Go to :ref:`guide-webhooks`.
 
 

+ 1 - 1
docs/userguide/tasks.rst

@@ -990,7 +990,7 @@ Make your design asynchronous instead, for example by using *callbacks*.
 Here we instead create a chain of tasks by linking together
 Here we instead create a chain of tasks by linking together
 different :func:`~celery.subtask`'s.
 different :func:`~celery.subtask`'s.
 You can read about chains and other powerful constructs
 You can read about chains and other powerful constructs
-at :ref:`designing-work-flows`.
+at :ref:`designing-workflows`.
 
 
 .. _task-performance-and-strategies:
 .. _task-performance-and-strategies:
 
 

+ 0 - 1
docs/userguide/tasksets.rst

@@ -1 +0,0 @@
-groups.rst