Skip to content

Commit

Permalink
Added all memcached error codes to spymemcached.
Browse files Browse the repository at this point in the history
Previously we only checked a subset of the error codes that
memcached can send back to a client. This created an issue because
if a user recieves an error code that it doesn't expect it resets
the connection. This commit adds all error codes.

Change-Id: I9317eccd688b6fa1e7113847cd7c7ef160a6624f
Reviewed-on: http://review.couchbase.org/7006
Reviewed-by: Michael Wiederhold <mike@couchbase.com>
Tested-by: Michael Wiederhold <mike@couchbase.com>
  • Loading branch information
Mike Wiederhold authored and Michael Wiederhold committed Jun 21, 2011
1 parent d6008b7 commit 6ce52d2
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 145 deletions.
Expand Up @@ -6,7 +6,6 @@
import net.spy.memcached.ops.ConcatenationOperation;
import net.spy.memcached.ops.ConcatenationType;
import net.spy.memcached.ops.OperationCallback;
import net.spy.memcached.ops.OperationStatus;

class ConcatenationOperationImpl extends OperationImpl
implements ConcatenationOperation {
Expand Down Expand Up @@ -44,27 +43,6 @@ public void initialize() {
prepareBuffer(key, cas, data);
}

@Override
protected OperationStatus getStatusForErrorCode(int errCode, byte[] errPl) {
OperationStatus baseStatus = super.getStatusForErrorCode(errCode, errPl);
if (baseStatus != null) {
return baseStatus;
}
OperationStatus rv=null;
switch(errCode) {
case ERR_EXISTS:
rv=EXISTS_STATUS;
break;
case ERR_NOT_FOUND:
rv=NOT_FOUND_STATUS;
break;
case ERR_NOT_STORED:
rv=NOT_FOUND_STATUS;
break;
}
return rv;
}

public Collection<String> getKeys() {
return Collections.singleton(key);
}
Expand Down
Expand Up @@ -5,7 +5,6 @@

import net.spy.memcached.ops.DeleteOperation;
import net.spy.memcached.ops.OperationCallback;
import net.spy.memcached.ops.OperationStatus;

class DeleteOperationImpl extends OperationImpl implements
DeleteOperation {
Expand All @@ -30,15 +29,6 @@ public void initialize() {
prepareBuffer(key, cas, EMPTY_BYTES);
}

@Override
protected OperationStatus getStatusForErrorCode(int errCode, byte[] errPl) {
OperationStatus baseStatus = super.getStatusForErrorCode(errCode, errPl);
if (baseStatus != null) {
return baseStatus;
}
return errCode == ERR_NOT_FOUND ? NOT_FOUND_STATUS : null;
}

public Collection<String> getKeys() {
return Collections.singleton(key);
}
Expand Down
Expand Up @@ -8,7 +8,6 @@
import net.spy.memcached.ops.GetlOperation;
import net.spy.memcached.ops.GetsOperation;
import net.spy.memcached.ops.OperationCallback;
import net.spy.memcached.ops.OperationStatus;

class GetOperationImpl extends OperationImpl
implements GetOperation, GetsOperation, GetlOperation,
Expand Down Expand Up @@ -84,15 +83,6 @@ protected void decodePayload(byte[] pl) {
getCallback().receivedStatus(STATUS_OK);
}

@Override
protected OperationStatus getStatusForErrorCode(int errCode, byte[] errPl) {
OperationStatus baseStatus = super.getStatusForErrorCode(errCode, errPl);
if (baseStatus != null) {
return baseStatus;
}
return errCode == ERR_NOT_FOUND ? NOT_FOUND_STATUS : null;
}

public Collection<String> getKeys() {
return Collections.singleton(key);
}
Expand Down
Expand Up @@ -46,15 +46,6 @@ public void initialize() {
prepareBuffer(key, 0, EMPTY_BYTES, by, defBytes, exp);
}

@Override
protected OperationStatus getStatusForErrorCode(int errCode, byte[] errPl) {
OperationStatus baseStatus = super.getStatusForErrorCode(errCode, errPl);
if (baseStatus != null) {
return baseStatus;
}
return errCode == ERR_NOT_FOUND ? NOT_FOUND_STATUS : null;
}

@Override
protected void decodePayload(byte[] pl) {
getCallback().receivedStatus(new OperationStatus(true,
Expand Down
73 changes: 43 additions & 30 deletions src/main/java/net/spy/memcached/protocol/binary/OperationImpl.java
Expand Up @@ -25,26 +25,23 @@ abstract class OperationImpl extends BaseOperationImpl implements Operation {
protected static final int MIN_RECV_PACKET=24;

/**
* Error code for items that were not found.
* Error code for operations.
*/
protected static final int SUCCESS = 0x00;
protected static final int ERR_NOT_FOUND = 0x01;
protected static final int ERR_EXISTS = 0x02;
protected static final int ERR_EINVAL = 0x04;
protected static final int ERR_2BIG = 0x03;
protected static final int ERR_INVAL = 0x04;
protected static final int ERR_NOT_STORED = 0x05;
protected static final int ERR_DELTA_BADVAL = 0x06;
protected static final int ERR_NOT_MY_VBUCKET = 0x07;
protected static final int ERR_UNKNOWN_COMMAND = 0x81;
protected static final int ERR_NO_MEM = 0x82;
protected static final int ERR_NOT_SUPPORTED = 0x83;
protected static final int ERR_INTERNAL = 0x84;
protected static final int ERR_BUSY = 0x85;
protected static final int ERR_TEMP_FAIL = 0x86;

protected static final OperationStatus NOT_FOUND_STATUS =
new CASOperationStatus(false, "Not Found", CASResponse.NOT_FOUND);
protected static final OperationStatus EXISTS_STATUS =
new CASOperationStatus(false, "Object exists", CASResponse.EXISTS);
protected static final OperationStatus NOT_STORED_STATUS =
new CASOperationStatus(false, "Not Stored", CASResponse.NOT_FOUND);
protected static final OperationStatus NOT_MY_VBUCKET_STATUS =
new OperationStatus(false, "Not my vbucket");
protected static final OperationStatus TEMP_FAIL =
new OperationStatus(false, "Temporary Error");

protected static final byte[] EMPTY_BYTES = new byte[0];

protected static final OperationStatus STATUS_OK =
Expand Down Expand Up @@ -148,20 +145,18 @@ public void readFromBuffer(ByteBuffer b) throws IOException {
}

protected void finishedPayload(byte[] pl) throws IOException {
if(errorCode != 0) {
OperationStatus status=getStatusForErrorCode(errorCode, pl);
if(status == null) {
handleError(OperationErrorType.SERVER, new String(pl));
} else if (status == NOT_MY_VBUCKET_STATUS && !getState().equals(OperationState.COMPLETE)) {
transitionState(OperationState.RETRY);
//errorCode = 0;
} else {
getCallback().receivedStatus(status);
transitionState(OperationState.COMPLETE);
}
} else {
OperationStatus status=getStatusForErrorCode(errorCode, pl);

if(status == null) {
handleError(OperationErrorType.SERVER, new String(pl));
} else if(errorCode == SUCCESS) {
decodePayload(pl);
transitionState(OperationState.COMPLETE);
} else if (errorCode == ERR_NOT_MY_VBUCKET && !getState().equals(OperationState.COMPLETE)) {
transitionState(OperationState.RETRY);
} else {
getCallback().receivedStatus(status);
transitionState(OperationState.COMPLETE);
}
}

Expand All @@ -172,11 +167,29 @@ protected void finishedPayload(byte[] pl) throws IOException {
* @return the status to return, or null if this is an exceptional case
*/
protected OperationStatus getStatusForErrorCode(int errCode, byte[] errPl) {
if (errCode == ERR_NOT_MY_VBUCKET) {
getLogger().warn("Not_my_vbucket on operation " + this);
return NOT_MY_VBUCKET_STATUS;
}
return null;
switch (errCode) {
case SUCCESS:
return STATUS_OK;
case ERR_NOT_FOUND:
return new CASOperationStatus(false, new String(errPl), CASResponse.NOT_FOUND);
case ERR_EXISTS:
return new CASOperationStatus(false, new String(errPl), CASResponse.EXISTS);
case ERR_NOT_STORED:
return new CASOperationStatus(false, new String(errPl), CASResponse.NOT_FOUND);
case ERR_2BIG:
case ERR_INVAL:
case ERR_DELTA_BADVAL:
case ERR_NOT_MY_VBUCKET:
case ERR_UNKNOWN_COMMAND:
case ERR_NO_MEM:
case ERR_NOT_SUPPORTED:
case ERR_INTERNAL:
case ERR_BUSY:
case ERR_TEMP_FAIL:
return new OperationStatus(false, new String(errPl));
default:
return null;
}
}

/**
Expand Down
Expand Up @@ -137,26 +137,6 @@ protected void finishedPayload(byte[] pl) throws IOException {
resetInput();
}

@Override
protected OperationStatus getStatusForErrorCode(int errCode, byte[] errPl) {
OperationStatus rv=null;
switch(errCode) {
case ERR_EXISTS:
rv=EXISTS_STATUS;
break;
case ERR_NOT_FOUND:
rv=NOT_FOUND_STATUS;
break;
case ERR_TEMP_FAIL:
rv=TEMP_FAIL;
break;
case ERR_NOT_MY_VBUCKET:
rv = NOT_MY_VBUCKET_STATUS;
break;
}
return rv;
}

@Override
protected boolean opaqueIsValid() {
return responseOpaque == terminalOpaque
Expand Down
Expand Up @@ -5,7 +5,6 @@

import net.spy.memcached.ops.CASOperation;
import net.spy.memcached.ops.OperationCallback;
import net.spy.memcached.ops.OperationStatus;
import net.spy.memcached.ops.StoreOperation;
import net.spy.memcached.ops.StoreType;

Expand Down Expand Up @@ -58,27 +57,6 @@ public void initialize() {
prepareBuffer(key, cas, data, flags, exp);
}

@Override
protected OperationStatus getStatusForErrorCode(int errCode, byte[] errPl) {
OperationStatus baseStatus = super.getStatusForErrorCode(errCode, errPl);
if (baseStatus != null) {
return baseStatus;
}
OperationStatus rv=null;
switch(errCode) {
case ERR_EXISTS:
rv=EXISTS_STATUS;
break;
case ERR_NOT_FOUND:
rv=NOT_FOUND_STATUS;
break;
case ERR_TEMP_FAIL:
rv=TEMP_FAIL;
break;
}
return rv;
}

public Collection<String> getKeys() {
return Collections.singleton(key);
}
Expand Down
Expand Up @@ -5,7 +5,6 @@

import net.spy.memcached.ops.KeyedOperation;
import net.spy.memcached.ops.OperationCallback;
import net.spy.memcached.ops.OperationStatus;

/**
* Operation to reset a timeout in Membase server.
Expand All @@ -29,27 +28,6 @@ public Collection<String> getKeys() {
return Collections.singleton(key);
}

@Override
protected OperationStatus getStatusForErrorCode(int errCode, byte[] errPl) {
OperationStatus baseStatus = super.getStatusForErrorCode(errCode, errPl);
if (baseStatus != null) {
return baseStatus;
}
OperationStatus rv=null;
switch(errCode) {
case ERR_EXISTS:
rv=EXISTS_STATUS;
break;
case ERR_NOT_FOUND:
rv=NOT_FOUND_STATUS;
break;
case ERR_TEMP_FAIL:
rv=TEMP_FAIL;
break;
}
return rv;
}

@Override
public void initialize() {
prepareBuffer(key, 0, EMPTY_BYTES, exp);
Expand Down
57 changes: 57 additions & 0 deletions src/test/java/net/spy/memcached/ErrorCodeTest.java
@@ -0,0 +1,57 @@
package net.spy.memcached;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map.Entry;

import net.spy.memcached.ops.GetOperation;
import net.spy.memcached.ops.OperationStatus;
import net.spy.memcached.protocol.binary.BinaryOperationFactory;
import junit.framework.TestCase;

public class ErrorCodeTest extends TestCase {

public void testErrorCodes() throws Exception {
HashMap<Byte, String> err_map = new HashMap<Byte, String>();
OperationFactory opFact = new BinaryOperationFactory();

err_map.put(new Byte((byte) 0x01), "NOT FOUND");
err_map.put(new Byte((byte) 0x02), "EXISTS");
err_map.put(new Byte((byte) 0x03), "2BIG");
err_map.put(new Byte((byte) 0x04), "INVAL");
err_map.put(new Byte((byte) 0x05), "NOT STORED");
err_map.put(new Byte((byte) 0x06), "DELTA BAD VAL");
err_map.put(new Byte((byte) 0x07), "NOT MY VBUCKET");
err_map.put(new Byte((byte) 0x81), "UNKNOWN COMMAND");
err_map.put(new Byte((byte) 0x82), "NO MEM");
err_map.put(new Byte((byte) 0x83), "NOT SUPPORTED");
err_map.put(new Byte((byte) 0x84), "INTERNAL ERROR");
err_map.put(new Byte((byte) 0x85), "BUSY");
err_map.put(new Byte((byte) 0x86), "TEMP FAIL");

int opaque = 0;
for (final Entry<Byte, String> err : err_map.entrySet()) {
byte[] b = new byte[24 + err.getValue().length()];
b[0] = (byte)0x81;
b[7] = err.getKey();
b[11] = (byte) err.getValue().length();
b[15] = (byte) ++opaque;
System.arraycopy(err.getValue().getBytes(), 0, b, 24, err.getValue().length());

GetOperation op=opFact.get("key",
new GetOperation.Callback() {
public void receivedStatus(OperationStatus s) {
assert !s.isSuccess();
assert err.getValue().equals(s.getMessage());
}
public void gotData(String k, int flags, byte[] data) {

}
public void complete() {
}});
ByteBuffer bb = ByteBuffer.wrap(b);
bb.flip();
op.readFromBuffer(bb);
}
}
}

0 comments on commit 6ce52d2

Please sign in to comment.