Handling Errors

  • how-to
    +
    Error handling from the Ruby SDK.

    Errors are inevitable. The developer’s job is to be prepared for whatever is likely to come up — and to try and be prepared for anything that conceivably could come up. Couchbase gives you a lot of flexibility, but it is recommended that you equip yourself with an understanding of the possibilities.

    How the SDK Handles Errors

    Couchbase-specific exceptions are all derived from Couchbase::Error::CouchbaseError. Errors that cannot be recovered by the SDK will be returned to the application. These unrecoverable errors are left to the application developer to handle — this section covers handling many of the common error scenarios.

    Handling Errors

    The approach will depend upon the type of error thrown. Is it transient? Is it even recoverable? Below we examine error handling strategies in relation to the Couchbase SDKs, then take a practical walk through some common error scenarios you are likely to have to handle when working with a Couchbase cluster.

    Failing

    While most of the time you want more sophisticated error handling strategies, sometimes you just need to fail. It makes no sense for some errors to be retried, either because they are not transient, or because you already tried everything to make it work and it still keeps failing. If containment is not able to handle the error, then it needs to propagate up to a parent component that can handle it.

    For synchronous programs, every error is converted into an Exception and thrown so that you can use regular begin/rescue semantics.

    If you do not catch the Exception, it will bubble up:

    Traceback (most recent call last):
    	4: from examples/crud.rb:21:in `<main>'
    	3: from lib/couchbase/cluster.rb:56:in `bucket'
    	2: from lib/couchbase/cluster.rb:56:in `new'
    	1: from lib/couchbase/bucket.rb:29:in `initialize'
    lib/couchbase/bucket.rb:29:in `open_bucket': unable open bucket "travel-sample": bucket_not_found (Couchbase::Error::BucketNotFound)

    Logging

    It is always important to log errors, but even more so in the case of reactive applications. Because of the event driven nature, stack traces get harder to look at, and caller context is sometimes lost.

    Refer to the Logging page for more details.

    Retry

    Transient errors — such as those caused by resource starvation — are best tackled with one of the following retry strategies:

    • Retry immediately.

    • Retry with a fixed delay.

    • Retry with a linearly increasing delay.

    • Retry with an exponentially increasing delay.

    • Retry with a random delay.

    KV

    The KV Service exposes several common errors that can be encountered — both during development, and to be handled by the production app. Here we will cover some of the most common errors.

    Document does not exist

    begin
      collection.replace("my-key", {"foo" => 42})
    rescue Couchbase::Error::DocumentNotFound => ex
      # key does not exist, report and continue
      puts ex
    end

    Document already exists

    begin
      collection.insert("my-key", {"foo" => 32})
    rescue Couchbase::Error::DocumentExists => ex
      # key already exists, report and continue
      puts ex
    end

    Document too large

    Couchbase::Error::ValueTooLarge

    CAS Mismatch

    begin
      result = collection.get("my-key")
      options = Couchbase::Collection::ReplaceOptions.new
      options.cas = result.cas
      collection.replace("my-key", {"foo" => 42}, options)
    rescue Couchbase::Error::CasMismatch => ex
      # the CAS value has changed
      puts ex
    end

    Durability ambiguous

    begin
      options = Couchbase::Collection::UpsertOptions.new
      options.durability_level = :persist_to_majority
      collection.upsert("my-key", {"foo" => 42}, options)
    rescue Couchbase::Error::DurabilityAmbiguous => ex
      # durable write request has not completed, it is unknown whether the request met the durability requirements or not
      puts ex
    end

    Durability invalid level

    begin
      options = Couchbase::Collection::UpsertOptions.new
      options.durability_level = :persist_to_majority
      collection.upsert("my-key", {"foo" => 42}, options)
    rescue Couchbase::Error::DurabilityLevelNotAvailable => ex
      # cluster not able to meet durability requirements
      puts ex
    end

    Query and Analytics Errors

    Query and Analytics either return results or an error.