Search code examples
mongodb

How to listen for changes to a MongoDB collection?


I'm creating a sort of background job queue system with MongoDB as the data store. How can I "listen" for inserts to a MongoDB collection before spawning workers to process the job?

Do I need to poll every few seconds to see if there are any changes from last time, or is there a way my script can wait for inserts to occur?

This is a PHP project that I am working on, but feel free to answer in Ruby or language agnostic.


Solution

  • MongoDB has what is called capped collections and tailable cursors that allows MongoDB to push data to the listeners.

    A capped collection is essentially a collection that is a fixed size and only allows insertions. Here's what it would look like to create one:

    db.createCollection("messages", { capped: true, size: 100000000 })
    

    MongoDB Tailable cursors (original post by Jonathan H. Wage)

    Ruby

    coll = db.collection('my_collection')
    cursor = Mongo::Cursor.new(coll, :tailable => true)
    loop do
      if doc = cursor.next_document
        puts doc
      else
        sleep 1
      end
    end
    

    PHP

    $mongo = new Mongo();
    $db = $mongo->selectDB('my_db')
    $coll = $db->selectCollection('my_collection');
    $cursor = $coll->find()->tailable(true);
    while (true) {
        if ($cursor->hasNext()) {
            $doc = $cursor->getNext();
            print_r($doc);
        } else {
            sleep(1);
        }
    }
    

    Python (by Robert Stewart)

    from pymongo import Connection
    import time
    
    db = Connection().my_db
    coll = db.my_collection
    cursor = coll.find(tailable=True)
    while cursor.alive:
        try:
            doc = cursor.next()
            print doc
        except StopIteration:
            time.sleep(1)
    

    Perl (by Max)

    use 5.010;
    
    use strict;
    use warnings;
    use MongoDB;
    
    my $db = MongoDB::Connection->new;
    my $coll = $db->my_db->my_collection;
    my $cursor = $coll->find->tailable(1);
    for (;;)
    {
        if (defined(my $doc = $cursor->next))
        {
            say $doc;
        }
        else
        {
            sleep 1;
        }
    }
    

    Additional Resources:

    Ruby/Node.js Tutorial which walks you through creating an application that listens to inserts in a MongoDB capped collection.

    An article talking about tailable cursors in more detail.

    PHP, Ruby, Python, and Perl examples of using tailable cursors.