Tuesday, June 26, 2012

New Relic Launches App Speed Index and Custom Dashboards


This is a guest blog post written and contributed by Bill Hodak, Director of Product Marketing at New Relic, an application performance management vendor and VM Farms partner.


New Relic is announcing the availability of two awesome new features. Thanks to our partnership, our customers have immediate access to these new features. When you login or sign up today, you’ll get one or both of these features.

New Relic and VM Farms have partnered to make New Relic Standard available to all VM Farms customers free of charge. If you’re not yet a customer, sign up today! All accounts start with 14 days of Pro, for free.


App Speed Index

Think your app is fast? Stop guessing and start knowing with the App Speed Index. The App Speed Index leverages our Big Data to provide Big Insight to our customers. New Relic collects over 55 billion performance metrics and monitors 1.5 billion page loads on behalf of our 25,000 customers and their 450,000 application instances. All of that data equates to 3.5 terabytes of new data collected, stored and analyzed each day.



With the App Speed Index, our customers will be able to classify their application into a Peer Group of similar applications (ex. eCommerce, SaaS, Gaming, and Consumer Internet applications) and benchmark their app with industry peers. Find out your percentile rank within your peer group for end user and application response times, error rates, and application availability to find out how fast you really are.

Learn more about the App Speed Index here, or check out this blog post. And don’t forget to check out our living infographic, updated daily to show how the peer groups rank by performance and availability. It even lists the fastest applications monitored by New Relic!

Custom Dashboards

Have you ever wanted to see Network I/O graphs and End User Response Time graphs on the same dashboard?  What about some custom business metrics and application response time? Now you can with Custom Dashboards. With Custom Dashboards you can build any dashboard with any data that tickles your fancy. The best part about it? No Coding Required!  With Custom Dashboards all you have to do is click and pick, drag and drop, or instant copy an existing New Relic graph and boom — you’ve got a Custom Dashboard. This feature is only available to our Pro customers. So if you’re not currently a Pro customer, sign up or upgrade today to get access to Custom Dashboards. Learn more about Custom Dashboards by reading this blog post.



Monday, October 31, 2011

How we solved the remote employee problem for less than $100 (and had a bit of fun)

UPDATE: We've given Rudy some serious upgrades and he's now mobile. Check out the new post here: http://blog.nibbler.io/2013/01/rudebot-rolling-ubiquitous-display.html

Problem: Running a startup and coordinating with a colleague that’s 4000km away. How do you remain nimble, keep the pace of ideas flowing, and maintain close working relationships without the overhead of initiating a conversation at a moments notice?




Teleworking is as common in the tech sector as the problems that accompany it. The physical separation and long periods of time between contact often result in significant wasted time. Wasted time in everything from warming up, to repeating explanations, to re-synchronizing efforts. A large organization may be able to navigate these issues by structuring their teams so that components of a project can be handled in multiple locations with well defined touch points and time periods, but this can be anywhere from frustrating to unworkable for a startup.

The theory was that the next best thing to having our colleague, Rudy, in our office, was having an open video line to him all day, effectively putting him in the office. We needed to test this idea to see if had any merit. We configured a spare laptop that was lying around, initiated a video chat, and placed it on a desk for a week.

The change in productivity was obvious to us even before the end of the first day. From our observation it was apparent that our conversational flow was no longer jilted or deflated by having to initiate a conference on the fly. More importantly, there were times where inertia or laziness prevented that call from being made. This was no longer an issue. Duplicate meetings weren’t happening anymore. Most crucially, Rudy was always a part of on-the-fly decision making, rather than simply being informed on the occasion that decisions had to be made quickly. Ultimately, this open channel improved the sociability of our work force. It’s a fact that your team is going to work better if there is a good bond between them. That’s not really an issue when you sit a few feet away from them, but much more challenging when communication is staggered, scheduled and strictly task oriented.

We let the experiment run to an end and were convinced we had our solution. We even had a few ideas on how to improve the experience. The biggest issue was his restricted field of view. We were constantly having to roll in front of the camera to have an interaction. We wanted to give Rudy the ability to rotate the laptop to whomever he was chatting with. This problem lent itself to a fairly simple solution that we thought we could put together rather quickly with an Arduino, a servo motor, a rotating platform, and a simple server that bridged communication between a serial port and TCP/IP port.


Tools List
  • Drill
  • 7/8" Drill Bit
  • 3/4" Hole Saw
  • Chisel
  • Scroll/Jig/Table Saw
  • Screwdriver
  • Square
  • Pencil
  • Vernier Caliper

Materials List
  • USB Cable (for powering Arduino+Servo)
  • 2'x2' 1/2" MDF
  • Arduino UNO
  • Hobby Servo Motor (with a stall torque of 5.2kg*cm or more @ 5v)
  • Plastic Circular Arm (included with servo or purchased separately)
  • 3x Jumper Wires
  • 1" piece of solid wood
  • Balls Bearing Swivel (sourced from Home Depot) 
  • Wood Glue
  • Finishing Nails
  • #6-3/4" Wood Screws


Method

  1. Use whatever carpentry skills you have to build a three sided box using 1/2" MDF. This will be the base for the rotating platform. The box should be dimensioned so that it's width and length match that of the laptop you will be using. Wood glue and finishing nails will do the trick.
  1. Locate the centre of the base and drill a 7/8" hole through it.
  1. Take your swivel and align it on the base so it is centered. Fasten it to the base with the screws provided.
  1. Rotate the swivel 45 degrees and mark on one side where the screw hole for the platform aligns with the base. You are doing this so you can drill a hole there so you can gain access from underneath to attach the platform.
  1. Drill the second hole.
  1. Cut out a new piece of MDF that matches the dimensions of the base's length and width.
  1. Attach the swivel back to the base with the 4 wood screws.
  1. Align the newly cut platform on the swivel with the base. Flip over the project and use the secondary hole to fasten the swivel to the platform.
  1. Test your work. Make sure that the swivel rotates freely and that the platform aligns with the base when everything is square.
  1. The fastening of the servo to the top platform through the base is a little tricky. If you're interested in how it was done, please get in touch with us and we'd be more than happy to explain it.

11. Plug your Arduino in to your PC and upload the following code:

#include <Servo.h> 

#define MAX_CHAR 4

Servo servo;                

char angle[MAX_CHAR];
char* p = angle;
int alen = 0;

int myatoi(const char *s, int *value)
{
    if ( s != NULL && *s != '\0' && value != NULL )
    {
        char *endptr = (char*)s;
        *value = (int)strtol(s, &endptr, 10);
        if ( *endptr == '\0' )
            return 1;
    }
    return 0; /* failed to convert string to integer */
}

void setup() 
{ 
  Serial.begin(115200);
  servo.attach(9);
  memset(angle, '\0', MAX_CHAR);
}

void loop() 
{ 
  if (Serial.available() > 0)
  {
    char c = NULL;
    
    while ((c = Serial.read()) >= 0) 
    {  
      if ( c == '\r' || c == '\n' )
      {
        int incr = 1;
        int newpos;
        int pos = servo.read();

        // Check to make sure the input is all ints      
        if (!myatoi(angle, &newpos))
          Serial.println("Error: Bad input");
        else if (newpos >= 0 && newpos <=180)
        {
          // Seal off the string
          *p = NULL;
          Serial.println("Input angle: " + String(angle));
          if ((newpos - pos) > 0)
            incr = 1;
          else if ((newpos - pos) < 0)
            incr = -1;
            
          for(int ipos = servo.read(); ipos != newpos; ipos += incr)
          {
            servo.write(ipos);
            delay(30);
          }
        }
        else
          Serial.println("Error: Bad angle");
      }
      else
      {          
        if (alen == MAX_CHAR-1)
          Serial.println("Error: Angle more than 3 bytes");
        else
        {
          *p++ = c;
          alen++;
          continue;
        }
      }
        
      // Reset our string
      p = angle;
      memset(angle, '\0', MAX_CHAR);
      alen = 0;
        
      // Chuck anything after a newline/cr
      Serial.flush();
      Serial.println("OK: Good to go");
    }
  }
}

12. Now attach your servo's signal pin to the Arduino board using digital IO pin #9. Jumper the V+ pin on the servo to the 5v pin on the Arduino. Lastly, attach the GND pins of the servo to the Arduino. You can now use a serial communications program like minicom to connect to your Arduino and issue angles as commands. As you can see in the code above, the baud rate is set to 115200, so make sure you adjust your serial parameters to match this.


13. The last step to make this remotely accessible was to use a python script that bridges a serial port to a TCP/IP socket. You can download that here. Executing it with the arguments below would most likely work for you. Replace XXXX with the path to your usb serial device and YYYY with the port you want the server to listen on.
./tcp_serial_redirect.py -b 115200 -p /dev/XXXX -P YYYY

Monday, October 24, 2011

How to use Deform in Django

Django comes with some great modules for form handling. You can have forms generated from models, create your own form classes, build form-sets, and plenty of other goodness. However, it doesn't have everything. Some of the widgets aren't the richest, form sequences can be difficult to create, logic can often end up spread out across multiple form objects for more complex forms, and your presentation is spread through templates and form objects.

We recently had a project come up where we needed all the form handling and presentation to be easily encapsulated into one central spot. We required that sequences, and sequences of sequences be supported. Additionally, we needed to develop these forms quickly and have the same rich validation functionality of the Django form module.

We looked high and low for a solution, and found a lot of lows. Then when we had a look at what the folks over at the Pylons project were doing and came across Deform: a kick-ass Python HTML form generation library. It is easy to understand and use, it includes a wide variety of widgets, has great error handling and validation support, and you can centralize all your code in one spot.

Check out this example from the docs(http://deformdemo.repoze.org/). This all the code required to create a form representing a sequence of sequences widgets:

class NameAndTitle(colander.Schema):
    name = colander.SchemaNode(colander.String())
    title = colander.SchemaNode(colander.String())
class NamesAndTitles(colander.SequenceSchema):
    name_and_title = NameAndTitle(title='Name and Title')
class NamesAndTitlesSequences(colander.SequenceSchema):
    names_and_titles = NamesAndTitles(title='Names and Titles')
class Schema(colander.Schema):
    names_and_titles_sequence = NamesAndTitlesSequences(
        title='Sequence of Sequences of Names and Titles')
schema = Schema()
form = deform.Form(schema, buttons=('submit',))
outer = form['names_and_titles_sequence']
outer.widget = deform.widget.SequenceWidget(min_len=1)
outer['names_and_titles'].widget = deform.widget.SequenceWidget(
    min_len=1)

Now, before we continue, a little primer. Deform heavily depends on the Colander library (also part of Pylons). Colander is a system for validating and deserializing data obtained via XML, JSON, an HTML form post or any other equally simple data serialization. Deform uses it to handle validation and schemas. So make sure you have the Colander docs open whenever you have the Deform docs open. This article is about integrating Deform into Django. Please see the Deform documentation for how to use Deform.

Great huh? Looks like we found just we needed, and the components of the Pylons project are loosely coupled, so we can drop it in and start using it right away:

pip install deform

However, there seems to be one little problem: Deform does not work in Django.

The Pylons project handles parsing of POST data a little differently then Django. Deform marks up generated forms with special hidden fields to delineate where sequence data begins, ends, and what order it is in. It also reuses these fields names. The problem is Django parses submitted forms into QueryDicts, and dicts don't preserve the order and the reused hidden field names. For Deform to be able to read the data, it needs to be in the form of a an ordered list of tuples representing the name and value pairs of the submitted form elements. This is a big problem. Luckily, after some searching we were able find simple workaround using standard off the shelf python libraries. We simply use the parse_qsl() function from the urlparse module. The best part is it returns the parsed form data exactly the way Deform needs it:

from urlparse import parse_qsl
controls = parse_qsl(request.raw_post_data, keep_blank_values=True)
appstruct = form.validate(controls)

We also need to make sure we change the form enctype in our HTML. The parse_qsl() function cannot parse multipart/form-data. Sorry folks, this means no file upload.

// This will seek out a form generated by deform and square it up
$('form#deform').attr('enctype', 'application/x-www-form-urlencoded');

That's it folks, you can now use Deform in your Django projects. Here is a full example of all the code required to integrate Deform into Django.

First the Python code:

from urlparse import parse_qsl
import colander
import deform
from deform.exception import ValidationFailure
from django.shortcuts import render_to_response

def test_deform(request):
    class NameAndTitle(colander.Schema):
        name = colander.SchemaNode(colander.String())
        title = colander.SchemaNode(colander.String())
    class NamesAndTitles(colander.SequenceSchema):
        name_and_title = NameAndTitle(title='Name and Title')
    class NamesAndTitlesSequences(colander.SequenceSchema):
        names_and_titles = NamesAndTitles(title='Names and Titles')
    class Schema(colander.Schema):
        names_and_titles_sequence = NamesAndTitlesSequences(
            title='Sequence of Sequences of Names and Titles')
    schema = Schema()
    form = deform.Form(schema, buttons=('submit',))
    outer = form['names_and_titles_sequence']
    outer.widget = deform.widget.SequenceWidget(min_len=1)
    outer['names_and_titles'].widget = deform.widget.SequenceWidget(
        min_len=1)
        
    if request.method == 'GET':        
        rendered = form.render()
    if request.method == 'POST':
        # Here is our hack from above
        controls = parse_qsl(request.raw_post_data, keep_blank_values=True)
        try:
            # If all goes well, deform returns a simple python structure of
            # the data. You use this same structure to populate a form with
            # data from permanent storage
            appstruct = form.validate(controls)
        except ValidationFailure, e:
            # The exception contains a reference to the form object
            rendered = e.render()
        else:
            # See how I am populating it with the appstruct
            rendered = form.render(appstruct)
    # Deform will do you the courtesy of telling you which dependencies it
    # needs. Be sure to copy the files from the deform directory to the media
    # directory of your server
    return render_to_response('deform.html', {
        'form': rendered,
        'deform_dependencies': form.get_widget_resources()
    })

Now the template:

<html>
    <head>
        <!-- Remember those dependencies from above -->
        {% for css in deform_dependencies.css %}
            <link rel="stylesheet" href="/deform/{{ css }}" type="text/css"/>
        {% endfor %}
        {% for js in deform_dependencies.js %}
            <script type="text/javascript" src="//deform/{{ js }}"></script>
        {% endfor %}
    </head>
    <body>
        <!-- Make sure not to escape the HTML output by Deform -->
        {{ form|safe }}

        <script type="text/javascript"><!--
        // Fix Deform's default enctype
        $('form#deform').attr('enctype', 'application/x-www-form-urlencoded');
        --></script>   
    </body>
</html>

All the documentation you need can be found here:

http://docs.pylonsproject.org/projects/deform/dev/
http://deformdemo.repoze.org/
http://docs.pylonsproject.org/projects/colander/dev/

Thanks goes out to the good folks at Pylons for their great work.

Thursday, April 28, 2011

It's getting cloudy out there

This post originally appeared on StartupNorth.ca.

I was approached by David Crow with an appealing proposition; we would help StartupNorth.ca by migrating their site to our infrastructure and have us manage it, and in return we will become a sponsor and have our logo placed on their site. You can read David’s post about all about it.

In addition I was also asked to be a guest author on the site and provide my thoughts on various topics on the subject of web operations and the cloud – a topic I am quite familiar with. I spent some time thinking about what I should write for my first post, but that decision was made for me late last week.


On April 21st, Amazon’s primary storage service (Elastic Block Store or EBS) in their North Virginia Availability Zone decided to kick the bucket. This failure is the equivalent of pulling out your computer’s hard drive while it’s running. The implications were likely dramatic for many businesses running on their cloud. As I watched many of my favorite sites (HootSuite and Reddit to name a couple) showcase their 500 error pages, I couldn’t help but think about how helpless the owners felt as Amazon worked hard to get their service back up and running.

I won’t get into the details of what happened with Amazon, as you can get a good hour-by-hour breakdown of events from Amazon directly (http://status.aws.amazon.com/). Instead, I’d like to outline some of my thoughts and take-away lessons.

This failure rekindled some old fears I’ve had since the sky started clouding over. Considering the amount of resources and engineering required to build massive multi-tenant infrastructures, equally massive failures only seem imminent. Don’t get me wrong, Amazon has done a phenomenal job building arguably the world’s largest and most successful cloud deployment. They have some of the best and brightest engineers this industry has to offer, but it’s still worth thinking twice about putting your business up in the cloud.

I’m reminded of a series of meetings I attended with my previous employer in which NetApp (one of the leaders in online storage) pitched their latest and greatest cloud storage solution. We were looking to build our own cloud and hence selecting the right storage solution was absolutely critical. As the well-informed NetApp architect diagramed the proposed architecture on a whiteboard, I was struck by how many “dependency lines” were connected to the NetApp unit. Inevitably the question came up of what would happen if the unit itself failed. The answer was simple: buy 2 of them. Simple answer, but effectively doubles the price tag. And buying 2 doesn’t necessarily prevent all types of failure.

Amazon was likely bit by one of these failures (post-mortem has yet to be released). Surprisingly, this nearly 5-day outage did not breach their 99.95% uptime SLA (99.95% is equivalent to roughly 4.38 hours of downtime per year). This is because the SLA is only applicable to their EC2 service, and not EBS (http://aws.amazon.com/ec2-sla/). Amazon is not legally obligated to reimburse any of its customers. Eventually though, most will likely forgive them since their service is so revolutionary compared to the old rack-and-stack hosting model.


Still, some harsh realities were realized this past week. So what can be learned from all this? Below I’ve identified 4 key points, which I bring up time and time again.

  1. Build for Failure.
    This is by far the most important point I emphasize. Whenever I advise my clients on a proposed architecture, I always ask the question “can you tolerate component X being down for a day?” Framing the question in that manner will highlight the realities of complicated systems – failure will happen, you need to plan for it. As a side note, I find it ironic that Amazon’s tightly coupled infrastructure requires application developers to do the opposite and build their application around Loosely Coupled Design. It is unfortunate that we need to change the way we deploy our applications to fit the provider but it is a necessary evil.
  2. Read the fine print carefully.
    Make sure you read through the SLA before you agree to the terms. This may seem obvious, but it’s surprising how many people gloss over this. Understand that uptime guarantees are usually not all encompassing, such as the case with Amazon.
  3. Explore alternatives.
    Not all cloud infrastructures are alike. The allure of the cloud is that it abstracts away the underlying implementation, but you should still do your homework and investigate the design decisions made by the vendor. The level of complexity should be factored into your decision making process. Like others, who worked out solutions to avoid such problems, we at VM Farms take a different approach to building redundancy into our Cloud.
  4. Identify your Achilles Heel.
    It helps to diagram your setup and draw dependency lines. If you notice any “hot spots” or “single points of failure” (SPOF), focus your engineering team on them and do what you can to mitigate those risks. Sometimes budgets or design decisions will prevent you from avoiding them. However, make sure you know what they are and incorporate this into your decision making process.
Instant resource availability will make any CTO’s mouth salivate. Just make sure you know what you’re getting into. When it rains, it pours.

Sunday, March 13, 2011

Mysteriously beautiful: using unicorns to manage Python server processes

When it comes to deploying Python servers (specifically web servers), there are no shortage of choices. Almost everyone agrees that WSGI is the right way to go, but the choice of a good container is less clear. For many projects, Apache + mod_wsgi is a fine choice that covers many web application deployment scenarios.

The situation becomes more complicated the moment real-time web application and asynchronous frameworks come to play. The ideal way to utilize multiple cores available on a modern server with Python is to use multiple processes. Some frameworks such as the excellent Tornado web framework recommend running one process per core, and using a reverse proxy such as nginx or haproxy to manage the process pool. This deployment scenario has in fact turned out to be very practical, with one exception: managing the process pool is not the most pleasant process. One possible approach is to use a process monitor such as supervisord, while others would just use the pre-forking capabilities built in to the framework.

Regardless of the nature of your app, pre-forking is probably your best choice; however, not all pre-forking applications are equal. If you're working on an application that is constantly being hammered at around 1000 requests / second, then you'd want a more reliable process management tool. gunicorn (green unicorn) fits the bill. It's extremely simple to setup, and configure. Most of the configuration is handled in a simple Python file and it's support for various containers, including async frameworks such as gevent, or Tornado, is excellent.

One of the most intriguing and useful options it offers is the ability to specify the number of requests that a worker process can serve before being "retired" in favour of a new worker process. In the gunicorn model, a master process is launched, whose sole purpose is to manage the child worker processes. Just as humans need to take a break after a long period of intense work, so do Python processes. Although it seems a bit "hacky", it's not an idea without merit. As the world is not perfect, neither is Python, nor is memory management, and let's be honest, nor is your code!

You can enable the auto-restart behaviour by settings the max_requests configuration parameter. Furthermore, if a worker process happens to die for any other reason, unicorns come to the rescue and make sure n workers are running as specified by the workers configuration parameter.
It's worth giving these mythical creatures a chance!

Wednesday, March 9, 2011

InnoDB hot backups with MySQL-ZRM for free!

Everyone knows how important it is to keep reliable database backups and to have them available and guaranteed to work when things go very wrong. This process has largely been automated by ops people everywhere... in hundreds of different ways, some with more complete feature sets than others. What about a complete solution that automates the entire process from backup to verification to restore?

Enter Zmanda MySQL Backup. If you're not familiar with the folks at Zmanda, they've been providing the community with backup solutions since 2006. They have an impressive and expansive offering, but what we're really impressed with is their backup and recovery manager for MySQL. It offers a complete and formalized solution to all our MySQL backup needs. Check it out:


Here at VM Farms we've gravitated towards using the excellent XtraDB offering from Percona. The performance, support and availability of an open-source hot backup solution over the standard InnoDB offering in MySQL makes this the ideal database solution for us. Check it out:


The beauty of Percona's XtraBackup utility is it works with both XtraDB and InnoDB (and also MyISAM with the help of some support scripts):


The only missing piece of the puzzle was combining Zmanda's MySQL solution with Percona's XtraBackup solution.

So, we bridged the gap. VM Farms announces the general availability of our own xtra-backup plugin for Zmanda, and we're releasing it with an open source license. Zmanda comes with support for MySQL's closed-source InnoDB hot-backup solution, so we used this hook to write a plugin for Zmanda that leverages Percona's solution for everyone out there who's been wondering why this didn't exist yet. Download it, use it, improve it, and enjoy the power of combing these two necessary tools:

Monday, March 7, 2011

Cross Process Locking and Synchronization in Python

Python includes all the bells and whistles to allow for multi-threading and multi-process locking and synchronization. However, the Python libraries lack many of the key features needed for true multi-process synchronization. We are going to take a trip through the Python source code(2.6.5 in our examples) to point out its shortcomings, and find a workaround to solve these problems.

First, a little primer. POSIX compliant systems have the necessary APIs to let the kernel do the work for you. The standard tool for cross-process synchronization is the “named semaphore”. Using them means more then one process can share the same semaphore (hence the naming, you got to find it somehow) with all the blocking (or non-blocking) fun handled by the API. These same APIs are used by Python to handle locking and synchronization in both the threading and multi-process libraries. However, Python only wraps some of their functionality, and leaves out the stuff you may need:

First, the threading module(/Python-2.6.5/Python/thread_pthread.h):

POSIX semaphores for legacy systems:
296         status = sem_init(lock,0,1);

POSIX thread mutexes for newer systems:
398         status = pthread_mutex_init(&lock->mut,
399                         pthread_mutexattr_default);

Depending on your system, either of these locking mechanisms are utilized by the Lock, RLock, Event, Condition, and Semaphore classes. Essentially, all of these classes are identical underneath. They all use an un-named binary POSIX semaphore for synchronization. This disqualifies them for use in any cross-process synchronization, since they are not named we cannot open them across processes. It is also useful to note they handle their own counting internally in pure Python (/Python-2.6.5/Lib/threading.py). That means all lock acquisition counting for re-entrant locks and non-binary semaphores in the threading library is handled in user space by Python, not kernel space.

Now the mulitprocess module (/Python-2.6.5/Modules/_multiprocessing/semaphore.c):

Straight up POSIX semaphores only here:
195 #define SEM_CREATE(name, val, max) sem_open(name, O_CREAT | O_EXCL, 0600, val)
.
.
.
439     handle = SEM_CREATE(buffer, value, maxvalue);

This looks more promising. It appears we can set the values and it takes a name. As well, just like the threading module the Lock, RLock, Event, Condition, and Semaphore classes in the multiprocessing module depend on this one central primitive. However, upon further review of the play, the naming of semaphores is not exposed by the Python multiprocessing interface, and is expressly forbid by the flags to the sem_open() library call in the Python source.

From the man pages:
If O_EXCL and O_CREAT are set, sem_open() fails if the semaphore name exists.

From the Python source:
195 #define SEM_CREATE(name, val, max) sem_open(name, O_CREAT | O_EXCL, 0600, val)

This is fine if we stay within the bounds of the process library; using the Process class to fork() new processes. Since they are all related, they will have access to the same Lock and their is no need to know the internal name the Python multiprocessing module named your semaphore. This great if you plan on spawning new processes off of existing ones.

However, if you have two or more independent processes (Python, or a mix of languages), how do you synchronize resources between the two? Python doesn’t expose named semaphores, and rolling your own takes a lot of work. Maybe another kernel controlled synchronization primitive will do. This leaves us with file locks. We can use them for our purposes, but without ever doing disk reads or writes.

Python exposes them through the fcntl module:

import os
import fcntl

class Lock:
    
    def __init__(self, filename):
        self.filename = filename
        # This will create it if it does not exist already
        self.handle = open(filename, 'w')
    
    # Bitwise OR fcntl.LOCK_NB if you need a non-blocking lock 
    def acquire(self):
        fcntl.flock(self.handle, fcntl.LOCK_EX)
        
    def release(self):
        fcntl.flock(self.handle, fcntl.LOCK_UN)
        
    def __del__(self):
        self.handle.close()

# Usage
try:
    lock = Lock("/tmp/lock_name.tmp")
    lock.acquire()
    # Do important stuff that needs to be synchronized
    .
    .
    .
finally: 
    lock.release()

As long as all your processes use that class in your Python code for locking, you will be fine. I find it works great for sharing resources between DJango, API’s, and regular Python processes. If you need to mix languages, just use flock() and be sure to use the same file name and to make sure you set it to blocking or non-blocking accordingly. It should also be very efficient, the file is created only when the lock is first created, and the locking is controlled from the kernel, so no more disk read/writes after instantiation.

Just remember that file locks are advisory. All programs must use flock() for it to work. As well, they are local, so if the file you are using as your lock file is on an NFS mount then it will only work for processes that are all located on the same system.

If you are interested in IPC and Python, I would suggest checking out POSIX IPC for Python. It exposes all kinds of goodness you may find useful, like shared memory and semaphores. However, at the time of the writing of this article it is currently on version 0.9.0 and little beta. I preferred to create a solution that was simple, and used the well tested and proven Python libraries.

If you need to scale your locking past one system I would suggest checking out Elock. It’s a distributed lock system created in Erlang, so you know it can scale. The adminstrative guide can be found on the main page and you can find a client example in Ruby here and in Python using Twisted here.

I would also suggest checking out this example that uses memcache. Great for distributed systems.