Caching Example for Python SDK

A walk-through of the basics of Key-Value operations with Couchbase, through the lens of a REST api caching layer.

This example uses Flask as a web-framework for a Python REST API. Flask has no direct support for async, but another code sample is available demonstrating this whole application using the Couchbase async API here. You can also find the full code for this example here.

Basic Endpoint

Our first basic endpoints will be a get and set call, using HTTP methods GET and POST and Couchbase methods get and insert respectively:

@app.route("/<key>", methods=["GET"])
def get(key):
    res = COLLECTION.get(key)
    return flask.jsonify(res.content_as[dict])

@app.route("/<key>", methods=["POST"])
def post(key):
    COLLECTION.insert(key, flask.request.json, expiration = EXPIRY)
    return "OK"

This is the simplest API we can make — allowing us to set and get arbitrary JSON from any key we specify. We also include the expiration parameter, which will automatically delete the document (invalidate the cache) after a set amount of time.

Cache Miss

There are many ways this can and should be improved upon for real world use. What happens in the case of a cache miss? With this code, our view throws an error and Flask returns us a HTTP 500 page. We can fix this by handling the KeyNotFoundException thrown by the get call; then we can either respond with a HTTP 404, or add a function to get a value from persistent storage:

@app.route("/<key>", methods=["GET"])
def get(key):
    try:
        res = COLLECTION.get(key)
        return flask.jsonify(res.content_as[dict])
    except KeyNotFoundException:
        val = getFromPersistent(key)
        return flask.jsonify(val)
        # alternatively...
        # return "Key not found", 404

Error Handling

We can also improve the POST function to deal with some of the errors it may encounter. Even if something unexpected happens, we can still be helpful by including the error in the 500 response, and by catching any CouchbaseError as a fallback:

@app.route("/<key>", methods=["POST"])
def post(key):
    try:
        COLLECTION.insert(key, flask.request.json, expiration = EXPIRY)
        return "OK"
    except KeyExistsException:
        return "Key already exists", 409
    except CouchbaseError as e:
        return "Unexpected error: {}".format(e), 500

The last thing we’ll do is add PUT and DELETE endpoints, matching up to the Couchbase operations upsert and remove, and apply the same error handling once more:

@app.route("/<key>", methods=["PUT"])
def put(key):
    try:
        COLLECTION.upsert(key, flask.request.json, expiration = EXPIRY)
        return "OK"
    except CouchbaseError as e:
        return "Unexpected error: {}".format(e), 500

@app.route("/<key>", methods=["DELETE"])
def delete(key):
    try:
        COLLECTION.remove(key)
        return "OK"
    except KeyNotFoundException:
        # Document already deleted / never existed
        return "Key does not exist", 404
    except CouchbaseError as e:
        return "Unexpected error: {}".format(e), 500

Additional Resources

  • You can find the full contextualized code from this sample here.

  • Selecting an ephemeral or Memcached bucket for the advantages of purely in-memory storage may make sense as a design decision.

  • A webinar, a whitepaper, and other high-level information on choosing Couchbase as a caching layer is on the main Couchbase website.