             return callback? err if err
             return @clear callback unless res
             @statistics callback if callback
+
+
+  # ##  Read-Only Operations
+
+  # ### Get Recently Finished Tasks Asynchronously
+
+  # Recently finished tasks are tasks stored in the `RECENT` list (in the
+  # reverse order of finished time), which will be limited to a maximum size
+  # default to 10.
+  #
+  # `recently_finished_tasks` is an asynchronous method. Arguments of the
+  # callback function follow node.js error handling convention: `err` and `res`.
+  #
+  # Below is an example `res` array:
+  #
+  #     [{ id:       '8c0c3eab-8114-41d6-8808-2ae8615d38b4',
+  #        params:   [ 'param1', 'param2' ],
+  #        queued:   Sat, 12 May 2012 07:41:33 GMT // Date Object
+  #        finished: Sat, 12 May 2012 07:41:59 GMT // Date Object
+  #      }, ...]
+  #
+  # **Usage:**
+  #
+  #     queue.recently_finished_tasks (err, tasks) -> YOUR CODE
+  recently_finished_tasks: (callback) =>
+    @redis.lrange @key('RECENT'), 0, -1, (err, res) ->
+      return callback err if err
+      callback null, res.map (entry) ->
+        entry = JSON.parse entry
+        id: entry[0]
+        params: entry[1..-3]
+        finished: new Date entry.pop()
+        queued: new Date entry.pop()
+
+
+  # ### Get Failed Tasks Asynchronously
+  
+  # Failed tasks are stored in the `FAILED` list.
+  #
+  # `failed_tasks` is an asynchronous method. Arguments of the callback function
+  # follow node.js convention: `err` and `res`.
+  # 
+  # Below is an example `res` array:
+  #
+  #     [{ id:     '8c0c3eab-8114-41d6-8808-2ae8615d38b4',
+  #        params: [ 'param1', 'param2' ],
+  #        queued: Sat, 12 May 2012 07:41:33 GMT // Date Object
+  #        failed: Sat, 12 May 2012 07:41:59 GMT // Date Object
+  #        reason: [ 'failure reason 1', 'failure reason 2', ...]
+  #      }, ...]
+  #
+  # **Usage:**
+  #
+  #     queue.failed_tasks (err, tasks) -> YOUR CODE
+  failed_tasks: (callback) =>
+    @redis.lrange @key('FAILED'), 0, -1, (err, res) ->
+      return callback err if err
+      callback null, res.map (entry) ->
+        entry = JSON.parse entry
+        id: entry[0]
+        params: entry[1..-4]
+        reason: entry.pop()
+        failed: new Date entry.pop()
+        queued: new Date entry.pop()
+
+
+  # ### Get Blocked Groups Asynchronously
+  
+  # Blocked groups' identifiers are stored in the `BLOCKED` set.
+  #
+  # `blocked_groups` is an asynchronous method. Arguments of the callback
+  # function follow node.js async callback pattern: `err` and `res`.
+  #
+  # Below is an example `res` array:
+  #
+  #     [ 'group1', 'group2', ...]
+  #
+  # **Usage:**
+  #
+  #     queue.blocked_groups (err, groups) -> YOUR CODE
+  blocked_groups: (callback) ->
+    @redis.smembers @key('BLOCKED'), (err, res) ->
+      return callback err if err
+      callback null, res.map (entry) ->
+        entry = JSON.parse entry
+
+
+  # ### Get Slowest Tasks Asynchronously
+
+  # Slowest tasks are tasks stored in the `SLOWEST` ordered set, which will be
+  # limited to a maximum size default to 10.
+  #
+  # `slowest_tasks` is an asynchronous method. Arguments of the callback
+  # function follow node.js error handling convention: `err` and `res`.
+  #
+  # Below is an example `res` array:
+  #
+  #     [{ id:      '8c0c3eab-8114-41d6-8808-2ae8615d38b4',
+  #        params:  [ 'param1', 'param2' ],
+  #        queued:  Sat, 12 May 2012 07:41:33 GMT // Date Object
+  #        started: Sat, 12 May 2012 07:41:39 GMT // Date Object
+  #        time:    1876 // time taken in milliseconds
+  #      }, ...]
+  #
+  # **Usage:**
+  #
+  #     queue.slowest_tasks (err, task) -> YOUR CODE
+  #
+  # `slowest_tasks` is an asynchronous method. The only arg of the callback
+  # function is an array of slowest tasks in the reverse order by processing
+  # time. The actual processing time will be appended at the end of the task's
+  # original arguments.
+  slowest_tasks: (callback) ->
+    @redis.zrevrange @key('SLOWEST'), 0, -1, "WITHSCORES", (err, res) ->
+      return callback err if err
+      res = res.map (entry) -> JSON.parse entry
+      callback null, ([res[i]...,res[i + 1]] for i in [0...res.length] by 2).map (entry) ->
+        id: entry[0]
+        params: entry[1..-4]
+        time: entry.pop()
+        started: new Date entry.pop()
+        queued: new Date entry.pop()
+
+
+  # ### Get Processing Tasks Asynchronously
+  
+  # Currently processing tasks are tasks in the `PROCESSING` list.
+  #
+  # `processing_tasks` is an asynchronous method. Arguments of the callback
+  # function follow node.js error handling convention: `err` and `res`.
+  #
+  # Below is an example `res` array:
+  #
+  #     [{ id:      '8c0c3eab-8114-41d6-8808-2ae8615d38b4',
+  #        params:  [ 'param1', 'param2' ],
+  #        queued:  Sat, 12 May 2012 07:41:33 GMT // Date Object
+  #        started: Sat, 12 May 2012 07:41:39 GMT // Date Object
+  #      }, ...]
+  #
+  # **Usage:**
+  #
+  #     queue.processing_tasks (err, tasks) -> YOUR CODE
+  processing_tasks: (callback) ->
+    @redis.hvals @key('PROCESSING'), (err, res) ->
+      return callback err if err
+      callback null, res.map (entry) ->
+        entry = JSON.parse(entry)
+        id: entry[0]
+        params: entry[1..-3]
+        start: new Date entry.pop()
+        queued: new Date entry.pop()
+
+
+
+  # ### Get Source Tasks Asynchronously
+
+  # Get tasks in the `SOURCE` list. **NOTE:** Tasks in the `SOURCE` list does
+  # **NOT** equal to **pending** tasks! There may be tasks in the `QUEUED` lists
+  # need be processed before processing tasks in the `SOURCE` list.
+  #
+  # Accepted parameters are:
+  #
+  #   1. `skip` *(optional)*, the number of skipped tasks. Defaults to 0.
+  #   2. `take` *(optional)*, the number of tasks need be taken. Defaults to 10.
+  #   3. `callback`, the callback function. Arguments of which follows node.js error
+  #   handling convention: `err` and `res`.
+  #
+  # Below is an example `res` array:
+  #
+  #     [{ id:     '8c0c3eab-8114-41d6-8808-2ae8615d38b4',
+  #        params: [ 'param1', 'param2' ],
+  #        queued: Sat, 12 May 2012 07:41:33 GMT // Date Object
+  #      }, ...]
+  #
+  # Possible combos of arguments are:
+  #
+  #   1. `callback`. (leave `skip` defaults to 0, `take` defaults to 10).
+  #   2. `skip` and `callback`. (leave `take` defaults to 10).
+  #   3. `skip`, `take`, and `callback`.
+  #
+  # **Usage:**
+  #
+  #     queue.source_tasks 20, 5, (err, tasks) -> YOUR CODE
+  source_tasks: (args..., callback) ->
+    skip = args[0] or 0
+    take = args[1] or 10
+    @redis.lrange @key('SOURCE'), skip, skip + take - 1, (err, res) ->
+      callback err if err
+      callback null, res.map (entry) ->
+        entry = JSON.parse entry
+        id: entry[0]
+        params: entry[1..-2]
+        queued: new Date entry.pop()
+
+
+  # ### Get Workers Asynchronously
+
+  # Asynchronous method to get all **live** workers of the queue. **Live**
+  # workers are registered in the `WORKERS` hash. Values of `WORKERS` hash are
+  # in `hostname|ip|pid|since` format.
+  #
+  # Arguments of the callback function follow node.js error handling convention:
+  # `err` and `res`. `res` is an array of live workers. Each worker object have:
+  #
+  #   + `host`, the host name of the worker machine.
+  #   + `ip`, the first external IPv4 address of the worker machine.
+  #   + `pid`, the process id of the working process.
+  #   + `since`, the born date of the worker.
+  #
+  # Below is an example of returned workers:
+  #
+  #     [{
+  #        host: 'baoshan',
+  #        ip: '192.168.2.7',
+  #        pid: 1628
+  #        since: Sat, 12 May 2012 07:28:21 GMT // Date Object
+  #      }, ...]
+  #
+  # **Usage:**
+  #
+  #     queue.workers (err, workers) -> YOUR CODE
+  workers: (callback) =>
+    @redis.hvals @key('WORKERS'), (err, res) ->
+      return callback err if err
+      callback null, res.map((entry) ->
+        entry = entry.split '|'
+        host: entry[0]
+        ip: entry[1]
+        pid: parseInt entry[2]
+        since: new Date parseInt entry[3]
+      ).sort (a, b) ->
+        return  1 if a.ip  > b.ip
+        return -1 if a.ip  < b.ip
+        return  1 if a.pid > b.pid
+        return -1 if a.pid < b.pid
 
 
   # ### Get Statistics of a Queue Asynchronously

#   + `uncaughtException` captured.
#
# If there's no registered workers, exit directly.
enter_cleanup_mode = ->
  if registered_workers.length
    log_registered_workers()
    cleanup_required = on
    exiting = on
  else
    return process.exit()

# When below signals are captured, gracefully exit the program by notifying all
# workers entering cleanup mode and exit after all are cleaned up.
#
# + `SIGINT` (e.g. `Control-C`)
# + `SIGHUP`
# + `SIGQUIT`
# + `SIGUSR1`
# + `SIGUSR2`
# + `SIGTERM`
# + `SIGABRT`
process.on 'SIGINT', enter_cleanup_mode
process.on 'SIGHUP', enter_cleanup_mode
process.on 'SIGQUIT', enter_cleanup_mode
process.on 'SIGUSR1', enter_cleanup_mode
process.on 'SIGUSR2', enter_cleanup_mode
process.on 'SIGTERM', enter_cleanup_mode
process.on 'SIGABRT', enter_cleanup_mode

# When `uncaughtException` is captured, **Fairy** can not tell if this is caught
# by the handling function, as well as which queue cause the exception.
# **Fairy** will fail all processing tasks and block the according group.
process.on 'uncaughtException', (err) ->
  console.log 'Exception:', err.stack
  console.log 'Fairy workers will block their processing groups before exit.' if registered_workers.length
  enter_cleanup_mode()

# Say goodbye on exit.
process.on 'exit', ->
  console.log "Fairy cleaned up, exiting..." if cleanup_required


# ## Helper Methods

                  requeued_tasks.push res...
                  start_transaction() unless --total_groups
            else start_transaction()


  # ### Clear A Queue
