Handling temporary out of memory errors

There may be cases where you are performing operations at such a high volume, that you need to decrease your requests to a rate your environment can handle. For instance Couchbase Server may return a temporary out of memory response based on a heavy request volume, your network can be slow, or your operations may be returning too many errors. You can handle this scenario by creating a loop that performs exponential backoff. With this technique, you have your process increasingly wait to a point if your requests stall.

One of the typically errors that may occur when you bulk load of data is that Couchbase Server returns an out of memory error due to the volume of requests. To handle this, you would follow this approach:

  • Create a loop that continuously tries your operation within a certain number of time limit, and possible a certain number of tries,

  • In the loop, attempt your operation,

  • If you get an out of memory error, have your process/thread wait,

  • Try the operation again,

  • If you get an error again, increase your wait time, and wait. This part of the approach is known as the exponential backoff.

This is a similar approach you could use for any Couchbase SDK, and for any operation you are performing in bulk, such as getting in bulk. The following example shows this approach using the Java SDK:

public OperationFuture<Boolean> contSet(String key, int exp, Object value,
          int tries) {
    OperationFuture<Boolean> result = null;
    OperationStatus status;
    int backoffexp = 0;

    try {
        do {
            if (backoffexp > tries) {
                throw new RuntimeException("Could not perform a set after "
                  + tries + " tries.");
             }

            result = cbc.set(key, exp, value);
            status = result.getStatus(); // blocking call

            if (status.isSuccess()) {
              break;
             }

            if (backoffexp > 0) {
              double backoffMillis = Math.pow(2, backoffexp);
              backoffMillis = Math.min(1000, backoffMillis); // 1 sec max
              Thread.sleep((int) backoffMillis);
              System.err.println("Backing off, tries so far: " + backoffexp);
            }

            backoffexp++;

            if (!status.isSuccess()) {
              System.err.println("Failed with status: " + status.getMessage());
            }

            } while (status.getMessage().equals("Temporary failure"));

    } catch (InterruptedException ex) {
        System.err.println("Interrupted while trying to set.  Exception:"
              + ex.getMessage());
    }

 }

In the first part of our try..catch loop, we have a do..while loop which continuously tries to set a key and value. In this loop we specify the amount of time the application waits, in milliseconds, as backoffMillis, and we increase it exponentially each time we receive a runtime exception. We will only exponentially increase the wait time a certain number of times, which we specify in the parameter, tries.

The other approach you can try if you get temporary out of memory errors from the server is to explicitly pace the timing of requests you make. You can do this in any SDK by creating a timer and only perform a Couchbase request after a specific timed interval. This will provide a slight delay between server requests and will reduce the risk of an out of memory error. For instance in Ruby:

c.set("foo", 100)
n = 1

c.run do
    c.create_periodic_timer(500000) do |tm|
        c.incr("foo") do
            if n == 5
                tm.cancel
      else
        n += 1
      end
    end
  end
end

In this example we create a sample record ‘foo’ with the initial fixnum value of 100. Then we create a increment count set to one, to indicate the first time we will create a Couchbase request. In the event loop, we create a timing loop that runs every.5 seconds until we have repeated the loop 5 times and our increment is equal to 5. In the timer loop, we increment ‘foo’ per loop.