<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: nosql</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/nosql.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2025-02-19T22:07:32+00:00</updated><author><name>Simon Willison</name></author><entry><title>Using S3 triggers to maintain a list of files in DynamoDB</title><link href="https://simonwillison.net/2025/Feb/19/s3-triggers/#atom-tag" rel="alternate"/><published>2025-02-19T22:07:32+00:00</published><updated>2025-02-19T22:07:32+00:00</updated><id>https://simonwillison.net/2025/Feb/19/s3-triggers/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://til.simonwillison.net/aws/s3-triggers-dynamodb"&gt;Using S3 triggers to maintain a list of files in DynamoDB&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I built an experimental prototype this morning of a system for efficiently tracking files that have been added to a large S3 bucket by maintaining a parallel DynamoDB table using S3 triggers and AWS lambda.&lt;/p&gt;
&lt;p&gt;I got 80% of the way there with this single prompt (complete with typos) to my &lt;a href="https://simonwillison.net/2024/Dec/19/one-shot-python-tools/#writing-these-with-the-help-of-a-claude-project"&gt;custom Claude Project&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Python CLI app using boto3 with commands for creating a new S3 bucket which it also configures to have S3 lambada event triggers which moantian a dynamodb table containing metadata about all of the files in that bucket. Include these commands&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;create_bucket - create a bucket and sets up the associated triggers and dynamo tables&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;list_files - shows me a list of files based purely on querying dynamo&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;ChatGPT then took me to the 95% point. The code Claude produced included an obvious bug, so I pasted the code into o3-mini-high on the basis that "reasoning" is often a great way to fix those kinds of errors:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Identify, explain and then fix any bugs in this code:&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;code from Claude pasted here&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;... and aside from adding a couple of &lt;code&gt;time.sleep()&lt;/code&gt; calls to work around timing errors with IAM policy distribution, &lt;a href="https://til.simonwillison.net/aws/s3-triggers-dynamodb#user-content-trying-it-out"&gt;everything worked&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Getting from a rough idea to a working proof of concept of something like this with less than 15 minutes of prompting is extraordinarily valuable.&lt;/p&gt;
&lt;p&gt;This is exactly the kind of project I've avoided in the past because of my almost irrational intolerance of the frustration involved in figuring out the individual details of each call to S3, IAM, AWS Lambda and DynamoDB.&lt;/p&gt;
&lt;p&gt;(Update: I just found out about &lt;a href="https://aws.amazon.com/about-aws/whats-new/2025/01/amazon-s3-metadata-generally-available/"&gt;the new S3 Metadata system&lt;/a&gt; which launched a few weeks ago and might solve this exact problem!)


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/aws"&gt;aws&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/lambda"&gt;lambda&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prototyping"&gt;prototyping&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/s3"&gt;s3&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chatgpt"&gt;chatgpt&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/uv"&gt;uv&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/o3"&gt;o3&lt;/a&gt;&lt;/p&gt;



</summary><category term="aws"/><category term="lambda"/><category term="nosql"/><category term="prototyping"/><category term="s3"/><category term="ai"/><category term="generative-ai"/><category term="chatgpt"/><category term="llms"/><category term="ai-assisted-programming"/><category term="claude"/><category term="uv"/><category term="o3"/></entry><entry><title>How Discord Stores Trillions of Messages</title><link href="https://simonwillison.net/2023/Mar/8/how-discord-stores-trillions-of-messages/#atom-tag" rel="alternate"/><published>2023-03-08T19:07:08+00:00</published><updated>2023-03-08T19:07:08+00:00</updated><id>https://simonwillison.net/2023/Mar/8/how-discord-stores-trillions-of-messages/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://discord.com/blog/how-discord-stores-trillions-of-messages"&gt;How Discord Stores Trillions of Messages&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
This is a really interesting case-study. Discord migrated from MongoDB to Cassandra &lt;a href="https://simonwillison.net/2021/Aug/24/how-discord-stores-billions-of-messages/"&gt;back in 2016&lt;/a&gt; to handle billions of messages. Today they're handling trillions, and they completed a migration from Cassandra to Scylla, a Cassandra-like data store written in C++ (as opposed to Cassandra's Java) to help avoid problems like GC pauses. In addition to being a really good scaling war story this has some interesting details about their increased usage of Rust. As a fan of request coalescing (which I've previously referred to as dogpile prevention) I particularly liked this bit:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Our data services sit between the API and our ScyllaDB clusters. They contain roughly one gRPC endpoint per database query and intentionally contain no business logic. The big feature our data services provide is request coalescing. If multiple users are requesting the same row at the same time, we’ll only query the database once. The first user that makes a request causes a worker task to spin up in the service. Subsequent requests will check for the existence of that task and subscribe to it. That worker task will query the database and return the row to all subscribers.&lt;/p&gt;
&lt;/blockquote&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://lobste.rs/s/sdttgb/how_discord_stores_trillions_messages"&gt;lobste.rs&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cassandra"&gt;cassandra&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dogpile"&gt;dogpile&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rust"&gt;rust&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/discord"&gt;discord&lt;/a&gt;&lt;/p&gt;



</summary><category term="cassandra"/><category term="dogpile"/><category term="nosql"/><category term="scaling"/><category term="rust"/><category term="discord"/></entry><entry><title>How Discord Stores Billions of Messages</title><link href="https://simonwillison.net/2021/Aug/24/how-discord-stores-billions-of-messages/#atom-tag" rel="alternate"/><published>2021-08-24T21:31:36+00:00</published><updated>2021-08-24T21:31:36+00:00</updated><id>https://simonwillison.net/2021/Aug/24/how-discord-stores-billions-of-messages/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blog.discord.com/how-discord-stores-billions-of-messages-7fa6ec7ee4c7"&gt;How Discord Stores Billions of Messages&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Fascinating article from 2017 describing how Discord migrated their primary message store to Cassandra (from MongoDB, but I could easily see them making the same decision if they had started with PostgreSQL or MySQL).&lt;/p&gt;
&lt;p&gt;The trick with scalable NoSQL databases like Cassandra is that you need to have a very deep understanding of the kinds of queries you will need to answer - and Discord had exactly that.&lt;/p&gt;
&lt;p&gt;In the article they talk about their desire to eventually migrate to Scylla (a compatible Cassandra alternative written in C++) - in the Hacker News comments &lt;a href="https://news.ycombinator.com/item?id=28292369#28293844"&gt;they confirm&lt;/a&gt; that in 2021 they are using Scylla for a few things but they still have their core messages in Cassandra.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=28292369"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cassandra"&gt;cassandra&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/discord"&gt;discord&lt;/a&gt;&lt;/p&gt;



</summary><category term="cassandra"/><category term="nosql"/><category term="scaling"/><category term="discord"/></entry><entry><title>NoSQL: What is the "best" solution for storing high volumes of structured data?</title><link href="https://simonwillison.net/2013/Nov/1/nosql-what-is-the/#atom-tag" rel="alternate"/><published>2013-11-01T18:45:00+00:00</published><updated>2013-11-01T18:45:00+00:00</updated><id>https://simonwillison.net/2013/Nov/1/nosql-what-is-the/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/NoSQL-What-is-the-best-solution-for-storing-high-volumes-of-structured-data/answer/Simon-Willison"&gt;NoSQL: What is the &amp;quot;best&amp;quot; solution for storing high volumes of structured data?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;On the right setup, PostgreSQL can handle petabytes. There are also commercial vendors such as Greenplum that offer data warehouse solutions built on a modified version of PostgreSQL.&lt;/p&gt;

&lt;p&gt;You should also take a look at Hadoop and Hive. Hive lets you use a language based heavily on SQL to construct map reduce queries which can then run against a Hadoop cluster.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/postgresql"&gt;postgresql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="databases"/><category term="nosql"/><category term="postgresql"/><category term="quora"/></entry><entry><title>How was FriendFeed's schema less db faster than pure MySQL?</title><link href="https://simonwillison.net/2013/Oct/30/how-was-friendfeeds-schema/#atom-tag" rel="alternate"/><published>2013-10-30T16:27:00+00:00</published><updated>2013-10-30T16:27:00+00:00</updated><id>https://simonwillison.net/2013/Oct/30/how-was-friendfeeds-schema/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/How-was-FriendFeeds-schema-less-db-faster-than-pure-MySQL?no_redirect=1"&gt;How was FriendFeed&amp;#39;s schema less db faster than pure MySQL?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The principle reason they switched to a schemaless DB was to work around the challenges of having to make schemes changes in MySQL, which can lock the table and take hours if bit days to complete in large tables.&lt;/p&gt;

&lt;p&gt;The performance improvement shown in the graph is almost certainly because they almost entirely eliminated joins and complex queries when they switched to the new mechanism. This meant that all it their database traffic was now simple queries, which have much more predictable performance characteristics. MySQL (in fact all databases) are extremely fast at primary key lookups and index scans.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/friendfeed"&gt;friendfeed&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rdbms"&gt;rdbms&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="databases"/><category term="friendfeed"/><category term="mysql"/><category term="nosql"/><category term="quora"/><category term="rdbms"/></entry><entry><title>How could we using couchbase with binary document as value?</title><link href="https://simonwillison.net/2013/Oct/20/how-could-we-using/#atom-tag" rel="alternate"/><published>2013-10-20T15:17:00+00:00</published><updated>2013-10-20T15:17:00+00:00</updated><id>https://simonwillison.net/2013/Oct/20/how-could-we-using/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/How-could-we-using-couchbase-with-binary-document-as-value/answer/Simon-Willison"&gt;How could we using couchbase with binary document as value?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There's a system called cbfs that acts as a distributed blobstore on top of Couchbase server - &lt;span&gt;&lt;a href="https://github.com/couchbaselabs/cbfs"&gt;https://github.com/couchbaselabs...&lt;/a&gt;&lt;/span&gt; - it looks like it is currently under active development.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/couchbase"&gt;couchbase&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="nosql"/><category term="quora"/><category term="couchbase"/></entry><entry><title>Any source available to download sample data (in 10+ GB) for testing?</title><link href="https://simonwillison.net/2012/Oct/15/any-source-available-to/#atom-tag" rel="alternate"/><published>2012-10-15T13:21:00+00:00</published><updated>2012-10-15T13:21:00+00:00</updated><id>https://simonwillison.net/2012/Oct/15/any-source-available-to/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Any-source-available-to-download-sample-data-in-10+-GB-for-testing/answer/Simon-Willison"&gt;Any source available to download sample data (in 10+ GB) for testing?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Wikipedia has some pretty interesting dumps, in both XML and SQL format: &lt;span&gt;&lt;a href="http://meta.wikimedia.org/wiki/Importing_a_Wikipedia_database_dump_into_MediaWiki"&gt;http://meta.wikimedia.org/wiki/I...&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;It's pretty easy to generate 10GB of random data for testing though, which may be a better option as you could better approximate the kind of data your application will be dealing with. There's a neat Ruby module for doing this called Faker (itself a port of the Perl module of the same name): &lt;span&gt;&lt;a href="http://faker.rubyforge.org/"&gt;http://faker.rubyforge.org/&lt;/a&gt;&lt;/span&gt; - and here's a Python port of the Ruby one: &lt;span&gt;&lt;a href="https://github.com/threadsafelabs/python-faker"&gt;https://github.com/threadsafelab...&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/programming"&gt;programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-development"&gt;web-development&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rdbms"&gt;rdbms&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="mysql"/><category term="nosql"/><category term="programming"/><category term="web-development"/><category term="quora"/><category term="rdbms"/></entry><entry><title>NoSQL: Whats the simplest on disk key-value storage?</title><link href="https://simonwillison.net/2012/Oct/4/nosql-whats-the-simplest/#atom-tag" rel="alternate"/><published>2012-10-04T15:15:00+00:00</published><updated>2012-10-04T15:15:00+00:00</updated><id>https://simonwillison.net/2012/Oct/4/nosql-whats-the-simplest/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/NoSQL-Whats-the-simplest-on-disk-key-value-storage/answer/Simon-Willison"&gt;NoSQL: Whats the simplest on disk key-value storage?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Surprisingly there doesn't seem to be an obvious answer to this. Here are a few options:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;MemcacheDB provides a Berkeley DB storage layer with a memcache protocol compatible interface. &lt;span&gt;&lt;a href="http://memcachedb.org/"&gt;http://memcachedb.org/&lt;/a&gt;&lt;/span&gt; - it hasn't been updated since 2008 though.&lt;/li&gt;&lt;li&gt;Tokyo Cabinet used to be a contender here, but by its own admission has now been superseded by Kyoto Cabinet. I don't know how widely used or mature Kyoto Cabinet is: &lt;span&gt;&lt;a href="http://fallabs.com/kyotocabinet/"&gt;http://fallabs.com/kyotocabinet/&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Google's leveldb library is an extremely fast, stable key-value storage library (it's used by Riak)... but on its own it doesn't come with a server. &lt;span&gt;&lt;a href="http://code.google.com/p/leveldb/"&gt;http://code.google.com/p/leveldb/&lt;/a&gt;&lt;/span&gt; - there is a leverdb-server project on github that adds the server layer but it doesn't look like it's particularly mature or actively maintained: &lt;span&gt;&lt;a href="https://github.com/srinikom/leveldb-server"&gt;https://github.com/srinikom/leve...&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;It might be worth looking at Riak - it may be over-kill for what you need, but it's definitely actively maintained and has an excellent reputation: &lt;span&gt;&lt;a href="http://wiki.basho.com/"&gt;http://wiki.basho.com/&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="nosql"/><category term="quora"/></entry><entry><title>What is the best NoSQL database to store unstructured data?</title><link href="https://simonwillison.net/2012/Feb/11/what-is-the-best/#atom-tag" rel="alternate"/><published>2012-02-11T15:06:00+00:00</published><updated>2012-02-11T15:06:00+00:00</updated><id>https://simonwillison.net/2012/Feb/11/what-is-the-best/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-is-the-best-NoSQL-database-to-store-unstructured-data/answer/Simon-Willison"&gt;What is the best NoSQL database to store unstructured data?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Any of the document stores are worth a look - I'd suggest investigating MongoDB, Riak and CouchDB.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="nosql"/><category term="quora"/></entry><entry><title>NoSQL: On a shared server, what are the alternatives to using SQL?</title><link href="https://simonwillison.net/2012/Feb/4/nosql-on-a-shared/#atom-tag" rel="alternate"/><published>2012-02-04T18:57:00+00:00</published><updated>2012-02-04T18:57:00+00:00</updated><id>https://simonwillison.net/2012/Feb/4/nosql-on-a-shared/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/NoSQL-On-a-shared-server-what-are-the-alternatives-to-using-SQL/answer/Simon-Willison"&gt;NoSQL: On a shared server, what are the alternatives to using SQL?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You could probably run Redis on a shared server - it doesn't need to be installed as root, but it does require a process to run all the time which shared hosts may not allow.&lt;/p&gt;

&lt;p&gt;There are a bunch of companies out there that offer hosted NoSQL as a service - &lt;span&gt;&lt;a href="https://mongohq.com/"&gt;https://mongohq.com/&lt;/a&gt;&lt;/span&gt; for example. You could talk to those from shared hosting.&lt;/p&gt;

&lt;p&gt;Your best bet though would be to upgrade from shared hosting to something more flexible. The bar for building an interesting web app has gone up, and you can get a basic VPS for $10/month - or use a platform like Heroku, &lt;span&gt;&lt;a href="http://ep.io"&gt;ep.io&lt;/a&gt;&lt;/span&gt; or dotCloud if you don't want to have to manage your own server.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="nosql"/><category term="quora"/></entry><entry><title>Benchmarks for scalability in NoSQL systems?</title><link href="https://simonwillison.net/2012/Jan/11/benchmarks-for-scalability-in/#atom-tag" rel="alternate"/><published>2012-01-11T10:28:00+00:00</published><updated>2012-01-11T10:28:00+00:00</updated><id>https://simonwillison.net/2012/Jan/11/benchmarks-for-scalability-in/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Benchmarks-for-scalability-in-NoSQL-systems/answer/Simon-Willison"&gt;Benchmarks for scalability in NoSQL systems?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;NoSQL systems are enormously varied which makes it hard (and not particularly constructive) to benchmark them against each other. How would you compare the performance of Redis, an in-memory data structure server, with Cassandra, a distributed redundant column store?&lt;/p&gt;

&lt;p&gt;By far the most useful benchmarks are the ones you run yourself against your own test data designed to reflect the specific workload of your own application.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="databases"/><category term="nosql"/><category term="quora"/></entry><entry><title>What are the best blogs about NoSQL?</title><link href="https://simonwillison.net/2011/Jan/7/what-are-the-best/#atom-tag" rel="alternate"/><published>2011-01-07T10:57:00+00:00</published><updated>2011-01-07T10:57:00+00:00</updated><id>https://simonwillison.net/2011/Jan/7/what-are-the-best/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-the-best-blogs-about-NoSQL/answer/Simon-Willison"&gt;What are the best blogs about NoSQL?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;myNoSQL is excellent: &lt;span&gt;&lt;a href="http://nosql.mypopescu.com/"&gt;http://nosql.mypopescu.com/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="nosql"/><category term="quora"/></entry><entry><title>What are the pros and cons of switching from MySQL to one of the NoSQL databases?</title><link href="https://simonwillison.net/2011/Jan/6/what-are-the-pros/#atom-tag" rel="alternate"/><published>2011-01-06T16:48:00+00:00</published><updated>2011-01-06T16:48:00+00:00</updated><id>https://simonwillison.net/2011/Jan/6/what-are-the-pros/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-the-pros-and-cons-of-switching-from-MySQL-to-one-of-the-NoSQL-databases/answer/Simon-Willison"&gt;What are the pros and cons of switching from MySQL to one of the NoSQL databases?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pro: If your own benchmarks tell you you need to switch to a specific NoSQL solution, you'll know exactly what the pro is.&lt;/p&gt;

&lt;p&gt;Pro: If you're doing something that's hard to model in a regular schema you might find it easier to use a document database such as CouchDB or MongoDB.&lt;/p&gt;

&lt;p&gt;Pro: Depending on how you approach the problem, you may find NoSQL makes schema modifications a LOT less painful than using a relational database.&lt;/p&gt;

&lt;p&gt;Con: For many projects, losing out on the relational model is a big disadvantage. Most NoSQL solutions require you to design your data storage with your queries in mind. When you are building a product you often don't know what kind of queries you are going to run. This has bitten me with AppEngine projects in the past. See also &lt;span&gt;&lt;a href="https://www.quora.com/What-did-Marissa-Mayer-mean-when-she-said-that-Orkut-failed-because-of-infrastructure-issues/answer/Edmond-Lau"&gt;Edmond Lau's answer to What did Marissa Mayer mean when she said that Orkut failed because of "infrastructure issues"?&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;For my money, the smart way of taking advantage of NoSQL is in conjunction with a relational engine. Use a regular database for your core data, but take advantage of Redis or MongoDB for things like counters, smart caches, rolling log storage etc. Polyglot persistence is the way to go.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="mysql"/><category term="nosql"/><category term="quora"/></entry><entry><title>What are the advantages and disadvantages of using MongoDB vs CouchDB vs Cassandra vs Redis?</title><link href="https://simonwillison.net/2010/Dec/1/what-are-the-advantages/#atom-tag" rel="alternate"/><published>2010-12-01T12:54:00+00:00</published><updated>2010-12-01T12:54:00+00:00</updated><id>https://simonwillison.net/2010/Dec/1/what-are-the-advantages/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-are-the-advantages-and-disadvantages-of-using-MongoDB-vs-CouchDB-vs-Cassandra-vs-Redis/answer/Simon-Willison"&gt;What are the advantages and disadvantages of using MongoDB vs CouchDB vs Cassandra vs Redis?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I see Redis as a different category from the other three - kind of like you wouldn't say "what are the advantages of MySQL v.s. Memcached". Redis makes an excellent complement to pretty much any other persistent storage mechanism. I expanded on this here: &lt;span&gt;&lt;a href="http://simonwillison.net/2009/Oct/22/redis/"&gt;http://simonwillison.net/2009/Oc...&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cassandra"&gt;cassandra&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/couchdb"&gt;couchdb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mongodb"&gt;mongodb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="cassandra"/><category term="couchdb"/><category term="mongodb"/><category term="nosql"/><category term="redis"/><category term="quora"/></entry><entry><title>Using MySQL as a NoSQL - A story for exceeding 750,000 qps on a commodity server</title><link href="https://simonwillison.net/2010/Oct/27/yoshinori/#atom-tag" rel="alternate"/><published>2010-10-27T23:10:00+00:00</published><updated>2010-10-27T23:10:00+00:00</updated><id>https://simonwillison.net/2010/Oct/27/yoshinori/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html"&gt;Using MySQL as a NoSQL - A story for exceeding 750,000 qps on a commodity server&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Very interesting approach: much of the speed difference between MySQL/InnoDB and memcached is due to the overhead involved in parsing and processing SQL, so the team at DeNA wrote their own MySQL plugin, HandlerSocket, which exposes a NoSQL-style network protocol for directly calling the low level MySQL storage engine APIs—resulting in a 7.5x performance increase.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/recovered"&gt;recovered&lt;/a&gt;&lt;/p&gt;



</summary><category term="mysql"/><category term="nosql"/><category term="scaling"/><category term="recovered"/></entry><entry><title>Will Redis support per-database persistence configuration?</title><link href="https://simonwillison.net/2010/Sep/27/will-redis-support-per-database/#atom-tag" rel="alternate"/><published>2010-09-27T10:37:00+00:00</published><updated>2010-09-27T10:37:00+00:00</updated><id>https://simonwillison.net/2010/Sep/27/will-redis-support-per-database/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Will-Redis-support-per-database-persistence-configuration/answer/Simon-Willison"&gt;Will Redis support per-database persistence configuration?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I don't know if that's on the roadmap (you'd need to ask antirez on the mailing list or Twitter), but it should be easy enough to run multiple Redis instances with different settings - especially on a multi core machine.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/programming"&gt;programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/software-engineering"&gt;software-engineering&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="nosql"/><category term="programming"/><category term="redis"/><category term="software-engineering"/><category term="quora"/></entry><entry><title>What is the largest production deployment of CouchDB for online use?</title><link href="https://simonwillison.net/2010/Aug/25/what-is-the-largest/#atom-tag" rel="alternate"/><published>2010-08-25T09:23:00+00:00</published><updated>2010-08-25T09:23:00+00:00</updated><id>https://simonwillison.net/2010/Aug/25/what-is-the-largest/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-is-the-largest-production-deployment-of-CouchDB-for-online-use/answer/Simon-Willison"&gt;What is the largest production deployment of CouchDB for online use?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The BBC have a pretty big CouchDB cluster, which they use mostly as a replicated key-value store. It's used by their new identity platform which includes customisation features for iPlayer.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/couchdb"&gt;couchdb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="couchdb"/><category term="databases"/><category term="nosql"/><category term="scaling"/><category term="quora"/></entry><entry><title>reddit's May 2010 "State of the Servers" report</title><link href="https://simonwillison.net/2010/May/18/cassandra/#atom-tag" rel="alternate"/><published>2010-05-18T18:37:00+00:00</published><updated>2010-05-18T18:37:00+00:00</updated><id>https://simonwillison.net/2010/May/18/cassandra/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://blog.reddit.com/2010/05/reddits-may-2010-state-of-servers.html"&gt;reddit&amp;#x27;s May 2010 &amp;quot;State of the Servers&amp;quot; report&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
An interesting Cassandra war story: Cassandra scales up, but it doesn’t scale down very well: running with just three nodes can make recovery from problems a lot more tricky.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cassandra"&gt;cassandra&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/reddit"&gt;reddit&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/recovered"&gt;recovered&lt;/a&gt;&lt;/p&gt;



</summary><category term="cassandra"/><category term="nosql"/><category term="reddit"/><category term="recovered"/></entry><entry><title>Comprehensive notes from my three hour Redis tutorial</title><link href="https://simonwillison.net/2010/Apr/25/redis/#atom-tag" rel="alternate"/><published>2010-04-25T22:36:16+00:00</published><updated>2010-04-25T22:36:16+00:00</updated><id>https://simonwillison.net/2010/Apr/25/redis/#atom-tag</id><summary type="html">
    &lt;p&gt;Last week I presented two talks at the inaugural &lt;a href="http://nosqleu.com/"&gt;NoSQL Europe&lt;/a&gt; conference in London. The first was presented with Matthew Wall and covered the ways in which we have been exploring &lt;a href="http://www.slideshare.net/matwall/nosql-presentation"&gt;NoSQL at the Guardian&lt;/a&gt;. The second was a three hour workshop on Redis, my favourite piece of software to have the NoSQL label applied to it.&lt;/p&gt;

&lt;p&gt;I've &lt;a href="http://simonwillison.net/2009/Oct/22/redis/"&gt;written about Redis&lt;/a&gt; here before, and it has since earned a place next to MySQL/PostgreSQL and memcached as part of my default web application stack. Redis makes write-heavy features such as real-time statistics feasible for small applications, while effortlessly scaling up to handle larger projects as well. If you haven't tried it out yet, you're sorely missing out.&lt;/p&gt;

&lt;p&gt;For the workshop, I tried to give an overview of each individual Redis feature along with detailed examples of real-world problems that the feature can help solve. I spent the past day annotating each slide with detailed notes, and I think the result makes a pretty good stand-alone tutorial. Here's the end result:&lt;/p&gt;

&lt;p&gt;&lt;a href="/static/2010/redis-tutorial/"&gt;Redis tutorial slides and notes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In unrelated news, Nat and I both completed the first ever &lt;a href="http://brightonmarathon.co.uk/"&gt;Brighton Marathon&lt;/a&gt; last weekend, in my case &lt;a href="http://www.sportsystems.co.uk/ss/results.htm?entId=WILLI-DJVYM-SIMNV"&gt;taking 4 hours, 55 minutes and 17 seconds&lt;/a&gt;. Sincere thanks to everyone who came out to support us - until the race I had never appreciated how important the support of the spectators is to keep going to the end. We &lt;a href="http://www.justgiving.com/natalie-simon-marathon-2010"&gt;raised £757&lt;/a&gt; for the &lt;a href="http://www.heart.co.uk/have-a-heart/"&gt;Have a Heart&lt;/a&gt; children's charity. Thanks in particular to &lt;a href="http://clearleft.com/"&gt;Clearleft&lt;/a&gt; who kindly offered to match every donation.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/brightonmarathon"&gt;brightonmarathon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/guardian"&gt;guardian&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/marathon"&gt;marathon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/running"&gt;running&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/my-talks"&gt;my-talks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/highlights"&gt;highlights&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/annotated-talks"&gt;annotated-talks&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="brightonmarathon"/><category term="guardian"/><category term="marathon"/><category term="nosql"/><category term="redis"/><category term="running"/><category term="my-talks"/><category term="highlights"/><category term="annotated-talks"/></entry><entry><title>Redis weekly update #3 - Pub/Sub and more</title><link href="https://simonwillison.net/2010/Mar/30/redis/#atom-tag" rel="alternate"/><published>2010-03-30T15:15:39+00:00</published><updated>2010-03-30T15:15:39+00:00</updated><id>https://simonwillison.net/2010/Mar/30/redis/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://antirez.com/post/redis-weekly-update-3-publish-submit.html"&gt;Redis weekly update #3 - Pub/Sub and more&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Redis is now a publish/subscribe server—and it ended up only taking 150 lines of C code since Redis internals were already based on that paradigm.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/c"&gt;c&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/pubsub"&gt;pubsub&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;&lt;/p&gt;



</summary><category term="c"/><category term="nosql"/><category term="pubsub"/><category term="redis"/></entry><entry><title>VMware: the new Redis home</title><link href="https://simonwillison.net/2010/Mar/16/vmware/#atom-tag" rel="alternate"/><published>2010-03-16T11:26:22+00:00</published><updated>2010-03-16T11:26:22+00:00</updated><id>https://simonwillison.net/2010/Mar/16/vmware/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://antirez.com/post/vmware-the-new-redis-home.html"&gt;VMware: the new Redis home&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Redis creator Salvatore Sanfilippo is joining VMWare to work on Redis full time. Sounds like a good match.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/keyvaluestores"&gt;keyvaluestores&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/salvatore-sanfilippo"&gt;salvatore-sanfilippo&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vmware"&gt;vmware&lt;/a&gt;&lt;/p&gt;



</summary><category term="keyvaluestores"/><category term="nosql"/><category term="redis"/><category term="salvatore-sanfilippo"/><category term="vmware"/></entry><entry><title>Redis weekly update #1 - Hashes and... many more!</title><link href="https://simonwillison.net/2010/Mar/13/redis/#atom-tag" rel="alternate"/><published>2010-03-13T00:06:22+00:00</published><updated>2010-03-13T00:06:22+00:00</updated><id>https://simonwillison.net/2010/Mar/13/redis/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://antirez.com/post/redis-weekly-update-1.html"&gt;Redis weekly update #1 - Hashes and... many more!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Hashes were the big missing data type in Redis—support is only partial at the moment (no ability to list all keys in a hash or delete a specific key) but at the rate Redis is developed I expect that to be fixed within a week or two.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/hashes"&gt;hashes&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/keyvaluestores"&gt;keyvaluestores&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;&lt;/p&gt;



</summary><category term="hashes"/><category term="keyvaluestores"/><category term="nosql"/><category term="redis"/></entry><entry><title>A Collection Of Redis Use Cases</title><link href="https://simonwillison.net/2010/Feb/16/paperplanes/#atom-tag" rel="alternate"/><published>2010-02-16T15:04:19+00:00</published><updated>2010-02-16T15:04:19+00:00</updated><id>https://simonwillison.net/2010/Feb/16/paperplanes/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://www.paperplanes.de/2010/2/16/a_collection_of_redis_use_cases.html"&gt;A Collection Of Redis Use Cases&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Lots of interesting case studies here, collated by Mathias Meyer. Redis clearly shines for anything involving statistics or high volumes of small writes.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/mathiasmeyer"&gt;mathiasmeyer&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;&lt;/p&gt;



</summary><category term="mathiasmeyer"/><category term="nosql"/><category term="redis"/></entry><entry><title>FleetDB</title><link href="https://simonwillison.net/2010/Jan/5/fleetdb/#atom-tag" rel="alternate"/><published>2010-01-05T11:21:35+00:00</published><updated>2010-01-05T11:21:35+00:00</updated><id>https://simonwillison.net/2010/Jan/5/fleetdb/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://fleetdb.org/"&gt;FleetDB&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Yet Another Key-Value Store: Schema-free, JSON protocol, everything cached in RAM, append-only log for durability, multi-record transactions... but what’s really interesting about this one is that it’s written in Clojure and takes full advantage of that language’s concurrency primitives. The prefix operators used by the select API hint at its Lisp heritage.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://news.ycombinator.com/item?id=1031540"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/clojure"&gt;clojure&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/databases"&gt;databases&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/fleetdb"&gt;fleetdb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/keyvaluestore"&gt;keyvaluestore&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/lisp"&gt;lisp&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;&lt;/p&gt;



</summary><category term="clojure"/><category term="databases"/><category term="fleetdb"/><category term="keyvaluestore"/><category term="lisp"/><category term="nosql"/></entry><entry><title>New Redis ZINCRBY command</title><link href="https://simonwillison.net/2009/Dec/22/zincrbycommand/#atom-tag" rel="alternate"/><published>2009-12-22T20:38:25+00:00</published><updated>2009-12-22T20:38:25+00:00</updated><id>https://simonwillison.net/2009/Dec/22/zincrbycommand/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://code.google.com/p/redis/wiki/ZincrbyCommand"&gt;New Redis ZINCRBY command&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Just added to Redis, a command which increments the “score” for an item in a sorted set and reorders the set to reflect the new scores. Looks ideally suited to real time stats, and I’m sure there are plenty of other exciting uses for it.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="http://twitter.com/antirez/status/6939773925"&gt;antirez&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/salvatore-sanfilippo"&gt;salvatore-sanfilippo&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sortedsets"&gt;sortedsets&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/zincrby"&gt;zincrby&lt;/a&gt;&lt;/p&gt;



</summary><category term="nosql"/><category term="redis"/><category term="salvatore-sanfilippo"/><category term="sortedsets"/><category term="zincrby"/></entry><entry><title>Crowdsourced document analysis and MP expenses</title><link href="https://simonwillison.net/2009/Dec/20/crowdsourcing/#atom-tag" rel="alternate"/><published>2009-12-20T12:07:53+00:00</published><updated>2009-12-20T12:07:53+00:00</updated><id>https://simonwillison.net/2009/Dec/20/crowdsourcing/#atom-tag</id><summary type="html">
    &lt;p&gt;As &lt;a href="https://web.archive.org/web/20091204154825/https://www.guardian.co.uk/politics/mps-expenses"&gt;you may have heard&lt;/a&gt;, the UK government released a fresh batch of MP expenses documents a week ago on Thursday. I spent that week working with a small team at Guardian HQ to prepare for the release. Here's what we built:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.archive.org/web/20091213164102/http://mps-expenses2.guardian.co.uk/"&gt;http://mps-expenses2.guardian.co.uk/&lt;/a&gt; &lt;em&gt;Updated March 2021: all links now go to the Internet Archive&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2009/mp-expenses-2-cropped.png" alt="Screenshot of the homepage from December 2019" style="max-width: 100%" /&gt;&lt;/p&gt;

&lt;p&gt;It's a crowdsourcing application that asks the public to help us dig through and categorise the enormous stack of documents - around 30,000 pages of claim forms, scanned receipts and hand-written letters, all scanned and published as PDFs.&lt;/p&gt;

&lt;p&gt;This is the second time we've tried this - the first was back in June, and can be seen at &lt;a href="https://web.archive.org/web/20090802094829/http://mps-expenses.guardian.co.uk/"&gt;mps-expenses.guardian.co.uk&lt;/a&gt;. Last week's attempt was an opportunity to apply the lessons we learnt the first time round.&lt;/p&gt;

&lt;p&gt;Writing crowdsourcing applications in a newspaper environment is a fascinating challenge. Projects have very little notice - I heard about the new document release the Thursday before giving less than a week to put everything together. In addition to the fast turnaround for the application itself, the 48 hours following the release are crucial. The news cycle moves fast, so if the application launches but we don't manage to get useful data out of it quickly the story will move on before we can impact it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.archive.org/web/20091124150940/http://www.scalecamp.org.uk/"&gt;ScaleCamp&lt;/a&gt; on the Friday meant that development work didn't properly kick off until Monday morning. The bulk of the work was performed by two server-side developers, one client-side developer, one designer and one QA on Monday, Tuesday and Wednesday. The Guardian operations team deftly handled our EC2 configuration and deployment, and we had some extra help on the day from other members of the technology department. After launch we also had a number of journalists helping highlight discoveries and dig through submissions.&lt;/p&gt;

&lt;p&gt;The system was written using Django, MySQL (InnoDB), Redis and memcached.&lt;/p&gt;

&lt;h4 id="asking-the-right-question"&gt;Asking the right question&lt;/h4&gt;

&lt;p&gt;The biggest mistake we made the first time round was that we asked the wrong question. We tried to get our audience to categorise documents as either "claims" or "receipts" and to rank them as "not interesting", "a bit interesting", "interesting but already known" and "someone should investigate this". We also asked users to optionally enter any numbers they saw on the page as categorised "line items", with the intention of adding these up later.&lt;/p&gt;

&lt;p&gt;The line items, with hindsight, were a mistake. 400,000 documents makes for a huge amount of data entry and for the figures to be useful we would need to confirm their accuracy. This would mean yet more rounds of crowdsourcing, and the job was so large that the chance of getting even one person to enter line items for each page rapidly diminished as the news story grew less prominent.&lt;/p&gt;

&lt;p&gt;The categorisations worked reasonably well but weren't particularly interesting - knowing if a document is a claim or receipt is useful only if you're going to collect line items. The "investigate this" button worked very well though.&lt;/p&gt;

&lt;p&gt;We completely changed our approach for the new system. We dropped the line item task and instead asked our users to categories each page by applying one or more tags, from a small set that our editors could control. This gave us a lot more flexibility - we changed the tags shortly before launch based on the characteristics of the documents - and had the potential to be a lot more fun as well. I'm particularly fond of the "hand-written" tag, which has highlighted some &lt;a href="https://web.archive.org/web/20091223091650/http://mps-expenses2.guardian.co.uk/page/1062/"&gt;lovely examples&lt;/a&gt; of correspondence between MPs and the expenses office.&lt;/p&gt;

&lt;p&gt;Sticking to an editorially assigned set of tags provided a powerful tool for directing people's investigations, and also ensured our users didn't start creating potentially libelous tags of their own.&lt;/p&gt;

&lt;h4 id="breaking-up-assignments"&gt;Breaking it up in to assignments&lt;/h4&gt;

&lt;p&gt;For the first project, everyone worked together on the same task to review all of the documents. This worked fine while the document set was small, but once we had loaded in 400,000+ pages the progress bar become quite depressing.&lt;/p&gt;

&lt;p&gt;This time round, we added a new concept of "&lt;a href="https://web.archive.org/web/20091215224727/http://mps-expenses2.guardian.co.uk/assignment/"&gt;assignments&lt;/a&gt;". Each assignment consisted of the set of pages belonging to a specified list of MPs, documents or political parties. Assignments had a threshold, so we could specify that a page must be reviewed by at least X people before it was considered reviewed. An editorial tool let us feature one "main" assignment and several alternative assignments right on the homepage.&lt;/p&gt;

&lt;p&gt;Clicking "start reviewing" on an assignment sets a cookie for that assignment, and adds the assignment's progress bar to the top of the review interface. New pages are selected at random from the set of unreviewed pages in that assignment.&lt;/p&gt;

&lt;p&gt;The assignments system proved extremely effective. We could use it to direct people to the highest value documents (our top hit list of interesting MPs, or members of the shadow cabinet) while still allowing people with specific interests to pick an alternative task.&lt;/p&gt;

&lt;h4 id="get-the-button-right"&gt;Get the button right!&lt;/h4&gt;

&lt;p&gt;Having run two crowdsourcing projects I can tell you this: the single most important piece of code you will write is the code that gives someone something new to review. Both of our projects had big "start reviewing" buttons. Both were broken in different ways.&lt;/p&gt;

&lt;p&gt;The first time round, the mistakes were around scalability. I used a SQL "ORDER BY RAND()" statement to return the next page to review. I knew this was an inefficient operation, but I assumed that it wouldn't matter since the button would only be clicked occasionally.&lt;/p&gt;

&lt;p&gt;Something like 90% of our database load turned out to be caused by that one SQL statement, and it only got worse as we loaded more pages in to the system. This caused multiple site slow downs and crashes until we threw together a cron job that pushed 1,000 unreviewed page IDs in to memcached and made the button pick one of those at random.&lt;/p&gt;

&lt;p&gt;This solved the performance problem, but meant that our user activity wasn't nearly as well targeted. For optimum efficiency you really want everyone to be looking at a different page - and a random distribution is almost certainly the easiest way to achieve that.&lt;/p&gt;

&lt;p&gt;The second time round I turned to my new favourite in-memory data structure server, &lt;a href="http://code.google.com/p/redis/"&gt;redis&lt;/a&gt;, and its &lt;a href="http://code.google.com/p/redis/wiki/SrandmemberCommand"&gt;SRANDMEMBER&lt;/a&gt; command (a feature I &lt;a href="http://twitter.com/simonw/status/5027987857"&gt;requested&lt;/a&gt; a while ago with this exact kind of project in mind). The system maintains a redis set of all IDs that needed to be reviewed for an assignment to be complete, and a separate set of IDs of all pages had been reviewed. It then uses redis set intersection (the &lt;a href="http://code.google.com/p/redis/wiki/SdiffstoreCommand"&gt;SDIFFSTORE&lt;/a&gt; command) to create a set of unreviewed pages for the current assignment and then SRANDMEMBER to pick one of those pages.&lt;/p&gt;

&lt;p&gt;This is where the bug crept in. Redis was just being used as an optimisation - the single point of truth for whether a page had been reviewed or not stayed as MySQL. I wrote a couple of Django management commands to repopulate the denormalised Redis sets should we need to manually modify the database. Unfortunately I missed some - the sets that tracked what pages were available in each document. The assignment generation code used an intersection of these sets to create the overall set of documents for that assignment. When we deleted some pages that had accidentally been imported twice I failed to update those sets.&lt;/p&gt;

&lt;p&gt;This meant the "next page" button would occasionally turn up a page that didn't exist. I had some very poorly considered fallback logic for that - if the random page didn't exist, the system would return the first page in that assignment instead. Unfortunately, this meant that when the assignment was down to the last four non-existent pages every single user was directed to the same page - which subsequently attracted well over a thousand individual reviews.&lt;/p&gt;

&lt;p&gt;Next time, I'm going to try and make the "next" button completely bullet proof! I'm also going to maintain a "denormalisation dictionary" documenting every denormalisation in the system in detail - such a thing would have saved me several hours of confused debugging.&lt;/p&gt;

&lt;h4 id="exposing-the-results"&gt;Exposing the results&lt;/h4&gt;

&lt;p&gt;The biggest mistake I made last time was not getting the data back out again fast enough for our reporters to effectively use it. It took 24 hours from the launch of the application to the moment the first reporting feature was added - mainly because we spent much of the intervening time figuring out the scaling issues.&lt;/p&gt;

&lt;p&gt;This time we handled this a lot better. We provided private pages exposing all recent activity on the site. We also provided public pages for each of the tags, as well as combination pages for party + tag, MP + tag, document + tag, assignment + tag and user + tag. Most of these pages were ordered by most-tagged, with the hope that the most interesting pages would quickly bubble to the top.&lt;/p&gt;

&lt;p&gt;This worked pretty well, but we made one key mistake. The way we were ordering pages meant that it was almost impossible to paginate through them and be sure that you had seen everything under a specific tag. If you're trying to keep track of everything going on in the site, reliable pagination is essential. The only way to get reliable pagination on a fast moving site is to order by the date something was first added to a set in ascending order. That way you can work through all of the pages, wait a bit, hit "refresh" and be able to continue paginating where you left off. Any other order results in the content of each page changing as new content comes in.&lt;/p&gt;

&lt;p&gt;We eventually added an undocumented /in-order/ URL prefix to address this issue. Next time I'll pay a lot more attention to getting the pagination options right from the start.&lt;/p&gt;

&lt;h4 id="rewarding-our-contributors"&gt;Rewarding our contributors&lt;/h4&gt;

&lt;p&gt;The reviewing experience the first time round was actually quite lonely. We deliberately avoided showing people how others had marked each page because we didn't want to bias the results. Unfortunately this meant the site felt like a bit of a ghost town, even when hundreds of other people were actively reviewing things at the same time.&lt;/p&gt;

&lt;p&gt;For the new version, we tried to provide a much better feeling of activity around the site. We added "top reviewer" tables to every assignment, MP and political party as well as a "most active reviewers in the past 48 hours" table on the homepage (this feature was added to the first project several days too late). User profile pages got a lot more attention, with more of a feel that users were collecting their favourite pages in to tag buckets within their profile.&lt;/p&gt;

&lt;p&gt;Most importantly, we added a concept of &lt;a href="https://web.archive.org/web/20091223091046/http://mps-expenses2.guardian.co.uk/discoveries/"&gt;discoveries&lt;/a&gt; - editorially highlighted pages that were shown on the homepage and credited to the user that had first highlighted them. These discoveries also added valuable editorial interest to the site, showing up on the homepage and also the index pages for &lt;a href="https://web.archive.org/web/20091215191906/http://mps-expenses2.guardian.co.uk/labour/"&gt;political parties&lt;/a&gt; and &lt;a href="https://web.archive.org/web/20091215050919/http://mps-expenses2.guardian.co.uk/conservative/gerald-howarth/"&gt;individual MPs&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id="light-weight-registration"&gt;Light-weight registration&lt;/h4&gt;

&lt;p&gt;For both projects, we implemented an extremely light-weight form of registration. Users can start reviewing pages without going through any signup mechanism, and instead are assigned a cookie and an anon-454 style username the first time they review a document. They are then encouraged to assign themselves a proper username and password so they can log in later and take credit for their discoveries.&lt;/p&gt;

&lt;p&gt;It's difficult to tell how effective this approach really is. I have a strong hunch that it dramatically increases the number of people who review at least one document, but without a formal A/B test it's hard to tell how true that is. The UI for this process in the first project was quite confusing - we gave it a solid makeover the second time round, which seems to have resulted in a higher number of conversions.&lt;/p&gt;

&lt;h4 id="overall-lessons"&gt;Overall lessons&lt;/h4&gt;

&lt;p&gt;News-based crowdsourcing projects of this nature are both challenging and an enormous amount of fun. For the best chances of success, be sure to ask the right question, ensure user contributions are rewarded, expose as much data as possible and make the "next thing to review" behaviour rock solid. I'm looking forward to the next opportunity to apply these lessons, although at this point I &lt;em&gt;really&lt;/em&gt; hope it involves something other than MPs' expenses.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/crowdsourcing"&gt;crowdsourcing&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/django"&gt;django&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/guardian"&gt;guardian&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/innodb"&gt;innodb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/memcached"&gt;memcached&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mpsexpenses"&gt;mpsexpenses&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mysql"&gt;mysql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/politics"&gt;politics&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="crowdsourcing"/><category term="django"/><category term="guardian"/><category term="innodb"/><category term="memcached"/><category term="mpsexpenses"/><category term="mysql"/><category term="nosql"/><category term="politics"/><category term="projects"/><category term="python"/><category term="redis"/></entry><entry><title>Node.js is genuinely exciting</title><link href="https://simonwillison.net/2009/Nov/23/node/#atom-tag" rel="alternate"/><published>2009-11-23T12:50:22+00:00</published><updated>2009-11-23T12:50:22+00:00</updated><id>https://simonwillison.net/2009/Nov/23/node/#atom-tag</id><summary type="html">
    &lt;p&gt;I gave a talk on Friday at &lt;a href="http://2009.full-frontal.org/"&gt;Full Frontal&lt;/a&gt;, a new one day JavaScript conference in my home town of Brighton. I ended up throwing away my intended topic (JSONP, APIs and cross-domain security) three days before the event in favour of a technology which first crossed my radar &lt;a href="http://simonwillison.net/2009/Nov/9/node/"&gt;less than two weeks ago&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That technology is Ryan Dahl's &lt;a href="http://nodejs.org/"&gt;Node&lt;/a&gt;. It's the most exciting new project I've come across in quite a while.&lt;/p&gt;

&lt;p&gt;At first glance, Node looks like yet another take on the idea of server-side JavaScript, but it's a lot more interesting than that. It builds on JavaScript's excellent support for event-based programming and uses it to create something that truly plays to the strengths of the language.&lt;/p&gt;

&lt;p&gt;Node describes itself as "evented I/O for V8 javascript". It's a toolkit for writing extremely high performance non-blocking event driven network servers in JavaScript. Think similar to &lt;a href="http://twistedmatrix.com/"&gt;Twisted&lt;/a&gt; or &lt;a href="http://rubyeventmachine.com/"&gt;EventMachine&lt;/a&gt; but for JavaScript instead of Python or Ruby.&lt;/p&gt;

&lt;h4&gt;Evented I/O?&lt;/h4&gt;

&lt;p&gt;As I discussed in my talk, event driven servers are a powerful alternative to the threading / blocking mechanism used by most popular server-side programming frameworks. Typical frameworks can only handle a small number of requests simultaneously, dictated by the number of server threads or processes available. Long-running operations can tie up one of those threads - enough long running operations at once and the server runs out of available threads and becomes unresponsive. For large amounts of traffic, each request must be handled as quickly as possible to free the thread up to deal with the next in line.&lt;/p&gt;

&lt;p&gt;This makes certain functionality extremely difficult to support. Examples include handling large file uploads, combining resources from multiple backend web APIs (which themselves can take an unpredictable amount of time to respond) or providing comet functionality by holding open the connection until a new event becomes available.&lt;/p&gt;

&lt;p&gt;Event driven programming takes advantage of the fact that network servers spend most of their time waiting for I/O operations to complete. Operations against in-memory data are incredibly fast, but anything that involves talking to the filesystem or over a network inevitably involves waiting around for a response.&lt;/p&gt;

&lt;p&gt;With Twisted, EventMachine and Node, the solution lies in specifying I/O operations in conjunction with callbacks. A single event loop rapidly switches between a list of tasks, firing off I/O operations and then moving on to service the next request. When the I/O returns, execution of that particular request is picked up again.&lt;/p&gt;

&lt;p&gt;(In the talk, I attempted to illustrate this with a questionable metaphor involving &lt;a href="http://www.slideshare.net/simon/evented-io-based-web-servers-explained-using-bunnies"&gt;hamsters, bunnies and a hyperactive squid&lt;/a&gt;).&lt;/p&gt;

&lt;iframe src="https://www.slideshare.net/slideshow/embed_code/key/B8ICSKJbZ2cBHw?startSlide=1" width="597" height="486" frameborder="0"   marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px;   margin-bottom:5px;max-width: 100%;" allowfullscreen="allowfullscreen"&gt;
&lt;/iframe&gt;

&lt;h4&gt;What makes Node exciting?&lt;/h4&gt;

&lt;p&gt;If systems like this already exist, what's so exciting about Node? Quite a few things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;JavaScript is extremely well suited to programming with callbacks&lt;/strong&gt;. Its anonymous function syntax and closure support is perfect for defining inline callbacks, and client-side development in general uses event-based programming as a matter of course: run this function when the user clicks here / when the Ajax response returns / when the page loads. JavaScript programmers already understand how to build software in this way.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Node represents a clean slate&lt;/strong&gt;. Twisted and EventMachine are hampered by the existence of a large number of blocking libraries for their respective languages. Part of the difficulty in learning those technologies is understanding which Python or Ruby libraries you can use and which ones you have to avoid. Node creator Ryan Dahl has a stated aim for Node to never provide a blocking API - even filesystem access and DNS lookups are catered for with non-blocking callback based APIs. This makes it much, much harder to screw things up.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Node is small&lt;/strong&gt;. I read through the &lt;a href="http://nodejs.org/api.html"&gt;API documentation&lt;/a&gt; in around half an hour and felt like I had a pretty comprehensive idea of what Node does and how I would achieve things with it.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Node is fast&lt;/strong&gt;. V8 is the fast and keeps getting faster. Node's event loop uses Marc Lehmann's highly regarded &lt;a href="http://software.schmorp.de/pkg/libev.html"&gt;libev&lt;/a&gt; and &lt;a href="http://software.schmorp.de/pkg/libeio.html"&gt;libeio&lt;/a&gt; libraries. Ryan Dahl is himself something of a speed demon - he just replaced Node's HTTP parser implementation (already pretty speedy due to it's Ragel / Mongrel heritage) with a &lt;a href="http://four.livejournal.com/1033160.html"&gt;hand-tuned C implementation&lt;/a&gt; with some impressive characteristics.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Easy to get started&lt;/strong&gt;. Node ships with all of its dependencies, and compiles cleanly on Snow Leopard out of the box.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With both my JavaScript and server-side hats on, Node just feels right. The APIs make sense, it fits a clear niche and despite its youth (the project started in February) everything feels solid and well constructed. The rapidly growing community is further indication that Ryan is on to something great here.&lt;/p&gt;

&lt;h4&gt;What does Node look like?&lt;/h4&gt;

&lt;p&gt;Here's how to get Hello World running in Node in 7 easy steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;samp&gt;git clone git://github.com/ry/node.git&lt;/samp&gt; (or download and extract &lt;a href="http://github.com/ry/node/archives/master" title="Download ry/node from GitHub"&gt;a tarball&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;samp&gt;./configure&lt;/samp&gt;&lt;/li&gt;
  &lt;li&gt;&lt;samp&gt;make&lt;/samp&gt; (takes a while, it needs to compile V8 as well)&lt;/li&gt;
  &lt;li&gt;&lt;samp&gt;sudo make install&lt;/samp&gt;&lt;/li&gt;
  &lt;li&gt;Save the below code as &lt;samp&gt;helloworld.js&lt;/samp&gt;&lt;/li&gt;
  &lt;li&gt;&lt;samp&gt;node helloworld.js&lt;/samp&gt;&lt;/li&gt;
  &lt;li&gt;Visit &lt;samp&gt;http://localhost:8080/&lt;/samp&gt; in your browser&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's helloworld.js:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var sys = require('sys'), 
  http = require('http');

http.createServer(function(req, res) {
  res.sendHeader(200, {'Content-Type': 'text/html'});
  res.sendBody('&amp;lt;h1&amp;gt;Hello World&amp;lt;/h1&amp;gt;');
  res.finish();
}).listen(8080);

sys.puts('Server running at http://127.0.0.1:8080/');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you have Apache Bench installed, try running &lt;samp&gt;ab -n 1000 -c 100 'http://127.0.0.1:8080/'&lt;/samp&gt; to test it with 1000 requests using 100 concurrent connections. On my MacBook Pro I get 3374 requests a second.&lt;/p&gt;

&lt;p&gt;So Node is fast - but where it really shines is concurrency with long running requests. Alter the helloworld.js server definition to look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;http.createServer(function(req, res) {
  setTimeout(function() {
    res.sendHeader(200, {'Content-Type': 'text/html'});
    res.sendBody('&amp;lt;h1&amp;gt;Hello World&amp;lt;/h1&amp;gt;');
    res.finish();
  }, 2000);
}).listen(8080);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We're using &lt;samp&gt;setTimeout&lt;/samp&gt; to introduce an artificial two second delay to each request. Run the benchmark again - I get 49.68 requests a second, with every single request taking between 2012 and 2022 ms. With a two second delay, the best possible performance for 1000 requests 100 at a time is  &lt;em&gt;1000 requests / (1000 / 100) * 2 seconds = 50 requests a second&lt;/em&gt;. Node hits it pretty much bang on the nose.&lt;/p&gt;

&lt;p&gt;The most important line in the above examples is &lt;code&gt;res.finish()&lt;/code&gt;. This is the mechanism Node provides for explicitly signalling that a request has been fully processed and should be returned to the browser. By making it explicit, Node makes it easy to implement comet patterns like long polling and streaming responses - stuff that is decidedly non trivial in most server-side frameworks.&lt;/p&gt;

&lt;h4&gt;djangode&lt;/h4&gt;

&lt;p&gt;Node's core APIs are pretty low level - it has HTTP client and server libraries, DNS handling, asynchronous file I/O etc, but it doesn't give you much in the way of high level web framework APIs. Unsurprisingly, this has lead to a cambrian explosion of lightweight web frameworks based on top of Node - the &lt;a href="http://wiki.github.com/ry/node"&gt;projects using node page&lt;/a&gt; lists a bunch of them. Rolling a framework is a great way of learning a low-level API, so I've thrown together my own - &lt;a href="http://github.com/simonw/djangode"&gt;djangode&lt;/a&gt; - which brings Django's regex-based URL handling to Node along with a few handy utility functions. Here's a simple djangode application:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var dj = require('./djangode');

var app = dj.makeApp([
  ['^/$', function(req, res) {
    dj.respond(res, 'Homepage');
  }],
  ['^/other$', function(req, res) {
    dj.respond(res, 'Other page');
  }],
  ['^/page/(\\d+)$', function(req, res, page) {
    dj.respond(res, 'Page ' + page);
  }]
]);
dj.serve(app, 8008);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;djangode is currently a throwaway prototype, but I'll probably be extending it with extra functionality as I explore more Node related ideas.&lt;/p&gt;

&lt;h4&gt;nodecast&lt;/h4&gt;

&lt;p&gt;My main demo in the Full Frontal talk was nodecast, an extremely simple broadcast-oriented comet application. Broadcast is my favourite "hello world" example for comet because it's both simpler than chat and more realistic - I've been involved in plenty of projects that could benefit from being able to broadcast events to their audience, but few that needed an interactive chat room.&lt;/p&gt;

&lt;p&gt;The source code for the version I demoed can be found on GitHub in &lt;a href="http://github.com/simonw/nodecast/tree/no-redis"&gt;the no-redis branch&lt;/a&gt;. It's a very simple application - the client-side JavaScript simply uses jQuery's getJSON method to perform long-polling against a simple URL endpoint:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;function fetchLatest() {
  $.getJSON('/wait?id=' + last_seen, function(d) {
    $.each(d, function() {
      last_seen = parseInt(this.id, 10) + 1;
      ul.prepend($('&amp;lt;li&amp;gt;&amp;lt;/li&amp;gt;').text(this.text));
    });
    fetchLatest();
  });
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Doing this recursively is probably a bad idea since it will eventually blow the browser's JavaScript stack, but it works OK for the demo.&lt;/p&gt;

&lt;p&gt;The more interesting part is the server-side &lt;samp&gt;/wait&lt;/samp&gt; URL which is being polled. Here's the relevant Node/djangode code:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var message_queue = new process.EventEmitter();

var app = dj.makeApp([
  // ...
  ['^/wait$', function(req, res) {
    var id = req.uri.params.id || 0;
    var messages = getMessagesSince(id);
    if (messages.length) {
      dj.respond(res, JSON.stringify(messages), 'text/plain');
    } else {
      // Wait for the next message
      var listener = message_queue.addListener('message', function() {
        dj.respond(res, 
          JSON.stringify(getMessagesSince(id)), 'text/plain'
        );
        message_queue.removeListener('message', listener);
        clearTimeout(timeout);
      });
      var timeout = setTimeout(function() {
        message_queue.removeListener('message', listener);
        dj.respond(res, JSON.stringify([]), 'text/plain');
      }, 10000);
    }
  }]
  // ...
]);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The wait endpoint checks for new messages and, if any exist, returns immediately. If there are no new messages it does two things: it hooks up a listener on the &lt;samp&gt;message_queue&lt;/samp&gt; EventEmitter (Node's equivalent of jQuery/YUI/Prototype's custom events) which will respond and end the request when a new message becomes available, and also sets a timeout that will cancel the listener and end the request after 10 seconds. This ensures that long polls don't go on too long and potentially cause problems - as far as the browser is concerned it's just talking to a JSON resource which takes up to ten seconds to load.&lt;/p&gt;

&lt;p&gt;When a message does become available, calling &lt;samp&gt;message_queue.emit('message')&lt;/samp&gt; will cause all waiting requests to respond with the latest set of messages.&lt;/p&gt;

&lt;h4&gt;Talking to databases&lt;/h4&gt;

&lt;p&gt;nodecast keeps track of messages using an in-memory JavaScript array, which works fine until you restart the server and lose everything. How do you implement persistent storage?&lt;/p&gt;

&lt;p&gt;For the moment, the easiest answer lies with the NoSQL ecosystem. Node's focus on non-blocking I/O makes it hard (but not impossible) to hook it up to regular database client libraries. Instead, it strongly favours databases that speak simple protocols over a TCP/IP socket - or even better, databases that communicate over HTTP. So far I've tried using CouchDB (with &lt;a href="http://github.com/sixtus/node-couch"&gt;node-couch&lt;/a&gt;) and redis (with &lt;a href="http://github.com/fictorial/redis-node-client"&gt;redis-node-client&lt;/a&gt;), and both worked extremely well. nodecast &lt;a href="http://github.com/simonw/nodecast"&gt;trunk&lt;/a&gt; now uses redis to store the message queue, and provides a nice example of working with a callback-based non-blocking database interface:&lt;/p&gt;

&lt;pre&gt;&lt;code class="javascript"&gt;var db = redis.create_client();
var REDIS_KEY = 'nodecast-queue';

function addMessage(msg, callback) {
  db.llen(REDIS_KEY, function(i) {
    msg.id = i; // ID is set to the queue length
    db.rpush(REDIS_KEY, JSON.stringify(msg), function() {
      message_queue.emit('message', msg);
      callback(msg);
    });
  });
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Relational databases are coming to Node. Ryan has a &lt;a href="http://github.com/ry/node_postgres"&gt;PostgreSQL adapter&lt;/a&gt; in the works, thanks to that database already featuring a mature non-blocking client library. MySQL will be a bit tougher - Node will need to grow a separate thread pool to integrate with the official client libs - but you can talk to MySQL right now by dropping in &lt;a href="https://open.nytimes.com/introducing-dbslayer-64d7168a143f"&gt;DBSlayer&lt;/a&gt; from the NY Times which provides an HTTP interface to a pool of MySQL servers.&lt;/p&gt;

&lt;h4&gt;Mixed environments&lt;/h4&gt;

&lt;p&gt;I don't see myself switching all of my server-side development over to JavaScript, but Node has definitely earned a place in my toolbox. It shouldn't be at all hard to mix Node in to an existing server-side environment - either by running both behind a single HTTP proxy (being event-based itself, &lt;a href="http://nginx.net/"&gt;nginx&lt;/a&gt; would be an obvious fit) or by putting Node applications on a separate subdomain. Node is a tempting option for anything involving comet, file uploads or even just mashing together potentially slow loading web APIs. Expect to hear a lot more about it in the future.&lt;/p&gt;

&lt;h4&gt;Further reading&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://s3.amazonaws.com/four.livejournal/20091117/jsconf.pdf"&gt;Ryan's JSConf.eu presentation&lt;/a&gt; is the best discussion I've seen anywhere of the design philosophy behind Node.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://nodejs.org/api.html"&gt;Node's API documentation&lt;/a&gt; is essential reading.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://debuggable.com/posts/streaming-file-uploads-with-node-js:4ac094b2-b6c8-4a7f-bd07-28accbdd56cb"&gt;Streaming file uploads with node.js&lt;/a&gt; illustrates how well suited Node is to accepting large file uploads.&lt;/li&gt;
  &lt;li&gt;&lt;a href="http://groups.google.com/group/nodejs"&gt;The nodejs Google Group&lt;/a&gt; is the hub of the Node community.&lt;/li&gt;
&lt;/ul&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/async"&gt;async&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/comet"&gt;comet&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/couchdb"&gt;couchdb&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/eventio"&gt;eventio&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/http"&gt;http&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nodejs"&gt;nodejs&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/redis"&gt;redis&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ryan-dahl"&gt;ryan-dahl&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/my-talks"&gt;my-talks&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/tornado"&gt;tornado&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/twisted"&gt;twisted&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/v8"&gt;v8&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/highlights"&gt;highlights&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/annotated-talks"&gt;annotated-talks&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="async"/><category term="comet"/><category term="couchdb"/><category term="eventio"/><category term="http"/><category term="javascript"/><category term="nodejs"/><category term="nosql"/><category term="redis"/><category term="ryan-dahl"/><category term="my-talks"/><category term="tornado"/><category term="twisted"/><category term="v8"/><category term="highlights"/><category term="annotated-talks"/></entry><entry><title>Quoting Matt Brubeck</title><link href="https://simonwillison.net/2009/Oct/4/sql/#atom-tag" rel="alternate"/><published>2009-10-04T09:50:33+00:00</published><updated>2009-10-04T09:50:33+00:00</updated><id>https://simonwillison.net/2009/Oct/4/sql/#atom-tag</id><summary type="html">
    &lt;blockquote cite="http://news.ycombinator.com/item?id=859617"&gt;&lt;p&gt;When I worked at Amazon.com we had a deeply-ingrained hatred for all of the SQL databases in our systems. Now, we knew perfectly well how to scale them through partitioning and other means. But making them highly available was another matter. Replication and failover give you basic reliability, but it's very limited and inflexible compared to a real distributed datastore with master-master replication, partition tolerance, consensus and/or eventual consistency, or other availability-oriented features.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="http://news.ycombinator.com/item?id=859617"&gt;Matt Brubeck&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/amazon"&gt;amazon&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/matt-brubeck"&gt;matt-brubeck&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/reliability"&gt;reliability&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/replication"&gt;replication&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/scaling"&gt;scaling&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/sql"&gt;sql&lt;/a&gt;&lt;/p&gt;



</summary><category term="amazon"/><category term="matt-brubeck"/><category term="nosql"/><category term="reliability"/><category term="replication"/><category term="scaling"/><category term="sql"/></entry><entry><title>Looking to the future with Cassandra</title><link href="https://simonwillison.net/2009/Sep/9/cassandra/#atom-tag" rel="alternate"/><published>2009-09-09T21:26:52+00:00</published><updated>2009-09-09T21:26:52+00:00</updated><id>https://simonwillison.net/2009/Sep/9/cassandra/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="http://blog.digg.com/?p=966"&gt;Looking to the future with Cassandra&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Digg are now using Cassandra for their “green badge” (one of your friends have dugg this story) feature—the resulting denormalised dataset weighs in at 3 TB and 76 billion columns.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/cassandra"&gt;cassandra&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/denormalisation"&gt;denormalisation&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/digg"&gt;digg&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/nosql"&gt;nosql&lt;/a&gt;&lt;/p&gt;



</summary><category term="cassandra"/><category term="denormalisation"/><category term="digg"/><category term="nosql"/></entry></feed>