   # Calls callback(error) on failure.
   # Calls callback() on success.
   @create = (docName, docData, callback) ->
+    compressor = new Compressor(options['compress'], options['timing'])
     async.auto(
-      write_metadata: (cb) ->
+      compress_meta: (cb) ->
+        compressor.encode(JSON.stringify(docData.meta), cb)
+
+      write_metadata: ['compress_meta', (cb, results) ->
         request =
           TableName: snapshots_table,
           Item:
             doc: { S: docName },
             v: { N: docData.v.toString() },
-            meta: { S: JSON.stringify(docData.meta) },
+            meta: { S: results.compress_meta },
             type: { S: docData.type },
             created_at: { N: new Date().getTime().toString() }
           Expected:
             doc:
               Exists: false
 
+        # Mark a snapshot as being compressed
+        request.Item['c'] = { S: 't' } if options['compress']
+
         snapshots_rw_queue.push((c) ->
           db.putItem(request, c)
         , 'write Snapshot('+docName+'-'+docData.v+')', cb)
-
-      write_data: (cb) ->
+      ]
+
+      compress_data: (cb) ->
+        compressor.encode(JSON.stringify(docData.snapshot), cb)
+
+      write_data: ['compress_data', (cb, results) ->
         path = snapshots_bucket+'/'+docName+'-'+docData.v+'.snapshot'
         headers = {}
-        data = JSON.stringify(docData.snapshot)
 
         s3_rw_queue.push((c) ->
-          s3.put(path, headers, data, c)
+          s3.put(path, headers, results.compress_data, c)
         , 'write Snapshot('+docName+'-'+docData.v+')', cb)
+      ]
     (error, results) ->
       if error?
         if error.message? and error.message.match 'The conditional request failed'

  # Calls callback(error) on failure.
  # Calls callback() on success.
  @writeSnapshot = (docName, docData, dbMeta, callback) ->
    async.auto(
      write_metadata: (cb) ->
        request =
          TableName: snapshots_table,
          Item:
            doc: { S: docName },
            v: { N: docData.v.toString() },
            meta: { S: JSON.stringify(docData.meta) },
            type: { S: docData.type },
            created_at: { N: new Date().getTime().toString() }
          Expected:
            doc:
              Exists: false

        snapshots_rw_queue.push((c) ->
          db.putItem(request, c)
        , 'write Snapshot('+docName+'-'+docData.v+')', cb)

      write_data: (cb) ->
        path = snapshots_bucket+'/'+docName+'-'+docData.v+'.snapshot'
        headers = {}
        data = JSON.stringify(docData.snapshot)

        s3_rw_queue.push((c) ->
          s3.put(path, headers, data, c)
        , 'write Snapshot('+docName+'-'+docData.v+')', cb)
    (error, results) ->
      if error?
        if error.message? and error.message.match 'The conditional request failed'

#  operations_ro_concurrency: 1
#  operations_rw_concurrency: 1
#
# If you would like more detailed information about the requests going to
# Amazon you can enabled timing information be setting the timing option to
# true.
#
# Example output:
# Dynamo[<number of milliseconds>,<cost in throughput of operation>] <type of operation>
# S3[<number of milliseconds>] <type of operation>
#
# The 'compress' option controls whether the JSON blobs are stored using gzip
# or not, it defaults to true. If this is disabled, there is a good chance that
# your operations might be larger than the 64KB limit of dynamo, you have been
# warned.

util = require('util')
async = require('async')
zlib = require('zlib')

defaultOptions =
  amazon_region: 'us-east-1'
  timing: false
  compress: true
  s3_rw_concurrency: 1
  s3_ro_concurrency: 1
  snapshots_ro_concurrency: 1
