Different CAS value for the same document obtained via Java SDK and N1QL

#1

Hello,

could somebody please try to explain why am I getting a CASMismatchException by running this simple piece of code?

JsonObject content = JsonObject.empty().put(“name”, “John”);
JsonDocument doc = JsonDocument.create(“docId”, content, 1452605940454916000L);
bucket.replace(doc);

The document “docId” does exist, the cas value matches the stored document (I obtained it separately via N1QL), the bucket used is an instance of CouchbaseBucket.

When I use

bucket.get(“docId”)

beforehand I get cas 1452605940454916096.

After replacing the document content via Java SDK the CAS value is still different:

Java SDK
1452689801718661120

N1QL
1452689801718661000

Maybe it’s a feature I haven’t read about in the documentation yet, but it seems a little odd. Thanks in advance for any answer.

J.

(Node SDK) N1qlQuery CAS invalid for document update
#2

Hi,

that doesn’t make sense to me. How can the CAS match the stored document if it does not exist?

Every time you replace a document, the CAS value is changed. Can you send over a piece of code that doesn’t do what you expect it to do? I bet its just a simple understanding issue.

#3

The document does exist, it was created before I tried the code snippet.

The question can be boiled down to why do I get different CAS values on:

  1. Java SDK - bucket.get(“docId”).cas()

  2. N1QL - SELECT meta(b).cas FROM bucketName b WHERE meta(b).id = “docId”

Might it be similar to this issue found in the Node.js SDK?

https://issues.couchbase.com/browse/JSCBC-196

J.

#4

Okay, thanks for the clarification. Is the document being mutated? So are you calling insert/upsert/replace before doing the get and/or the SELECT?

1 Like
#5

No change to the document between the Java get and the N1QL select, just using two different ways to obtain the CAS at the same time.

As I mentioned in my first post, the CAS values look similar, just last two or three digits are different.

I tried N1QL via REST, Query Workbench and Java SDK (bucket.query(N1qlQuery)) with the same result - the CAS is different from the one obtained via Java SDK (bucket.get(String)).

J.

#6

Have you tried with consistency on the N1QL query? Like that:

bucket.query(n1qlQuery, N1qlParams.build().consistency(ScanConsistency.REQUEST_PLUS));
#7

@juraj_durech CAS is meant to be opaque, so even its a little different its considered “different”.

#8

Yes, same result, the CAS values are different.

J.

#9

Yes, I agree, according Couchbase concepts the CAS doesn’t have any parts, it has to be equal as a whole number.

What I was trying to suggest is that if the CAS value differs just slightly (last digits) when obtained by Java SDK vs N1QL. This maybe points to a small rounding/encoding/transformation bug. You know best how the CAS is stored/calculated internally and also how is it propagated to the outside world :slightly_smiling:

J.

#10

Hm, do you think it would be possible so that you assemble a small standalone program for us to reproduce? I’d be supper happy to see that :slightly_smiling:

#11

I’ve put together a small maven project which shows the discrepancy, just run com.couchbase.test.cas.Main.main.

It’s using the newest 2.2.3 Java SDK but the behavior was the same on 2.2.2. Couchbase is running locally on my Windows 7 64bit, server version 4.1.0-5005 Enterprise Edition (build-5005), it uses a TEST bucket with a primary index, JDK version 1.8.0_51.

J.

cas.zip (9.5 KB)

#12

Couldn’t download your attachment…
Here is what I tried:

public class CasN1qlVsGet {

    public static void main(String[] args) {
        Cluster cluster = CouchbaseCluster.create();
        Bucket bucket = cluster.openBucket();
        try { bucket.remove("testCas"); } catch (Exception e) {}

        long initialCas = bucket.insert(JsonDocument.create("testCas")).cas();
        long updatedCas = bucket.replace(JsonDocument.create("testCas", JsonObject.create().put("value", "mutated"))).cas();

        N1qlQuery casQuery1 = N1qlQuery.simple("SELECT META(default).cas AS cas FROM default WHERE META(default).id = \"testCas\"");
        N1qlQuery casQuery2 = N1qlQuery.simple("SELECT META(default).cas AS cas FROM default USE KEYS \"testCas\"");

        long n1qlCasWhere = exec(bucket, casQuery1);
        long n1qlCasUseKeys = exec(bucket, casQuery2);

        long getCas = bucket.get("testCas").cas();

        System.out.println("CREATION     : " + initialCas);
        System.out.println("UPDATE       : " + updatedCas);
        System.out.println("N1QL WHERE   : " + n1qlCasWhere);
        System.out.println("N1QL USE KEYS: " + n1qlCasUseKeys);
        System.out.println("GET          : " + getCas);
    }

    private static long exec(Bucket bucket, N1qlQuery query) {
        N1qlQueryResult result = bucket.query(query);
        if (!result.finalSuccess()) {
            System.err.println(result.errors());
            return -1L;
        }
        if (result.allRows().isEmpty()) {
            System.err.println("Empty rows");
            return -2L;
        }
        return result.allRows().get(0).value().getLong("cas");
    }
}

This outputs:

CREATION     : 328484819173376
UPDATE       : 328484827496448
N1QL WHERE   : 328484827496448
N1QL USE KEYS: 328484827496448
GET          : 328484827496448

Note that if no primary index exist, WHERE flavor will fail whereas USE KEYS flavor will still perform:

[{"msg":"No primary index on keyspace default. Use CREATE PRIMARY INDEX to create one.","code":4000}]
CREATION     : 328383548882944
UPDATE       : 328383558451200
N1QL WHERE   : -1
N1QL USE KEYS: 328383558451200
GET          : 328383558451200

Didn’t even use a particular scan consistency on the N1QL queries, and cas are the same.

#13

I’ve tried your example on my machine with the following result

CREATION     : 1452770685887840256
UPDATE       : 1452770685895835648
N1QL WHERE   : 1452770685895835600
N1QL USE KEYS: 1452770685895835600
GET          : 1452770685895835648

Here’s the code I’ve sent previously

public class Main {

	static Cluster cluster = null;
	static String documentId = null;
	static long casViaJavaSDK = 0L;
	static long casViaN1QL = 0L;

	public static void main(String[] args) {
		try {
			cluster = CouchbaseCluster.create("127.0.0.1");
			Bucket bucket = cluster.openBucket("default");
			documentId = UUID.randomUUID().toString();

			// new document inserted
			JsonObject content = JsonObject.empty().put("name", "Test");
			JsonDocument document = JsonDocument.create(documentId, content);
			bucket.insert(document);

			// CAS read via Java SDK
			JsonDocument documentRead = bucket.get(documentId);
			casViaJavaSDK = documentRead.cas();

			// CAS read via N1QL
			N1qlQuery query = N1qlQuery.simple("SELECT meta(`t`).`cas` FROM `default` `t` WHERE meta(`t`).`id` = \"" + documentId + "\"");
			query.params().consistency(ScanConsistency.REQUEST_PLUS);
			N1qlQueryResult queryResult = bucket.query(query);
			casViaN1QL = queryResult.allRows().get(0).value().getLong("cas");

			// comparison
			System.out.println("casViaJavaSDK\t" + casViaJavaSDK);
			System.out.println("casViaN1QL\t" + casViaN1QL);
		} finally {
			if (cluster != null) {
				cluster.disconnect();
			}
		}
	}
}

Sample result on my machine

casViaJavaSDK	1452771133736484864
casViaN1QL	1452771133736484900

Btw. you get the same CAS via Java SDK and N1QL every 20-35 runs :slightly_smiling:

J.

#14

Mmh I think you indeed just stumbled upon a rounding bug of some sort, my friend :confused:
(calling the bug police :cop::bug: @geraldss )

In cbq, just try to do a SELECT 1452770685895835648; and look at the result, last two digits are replaced with 0:

cbq> SELECT 1452770685895835648;
{
    "requestID": "e6237c6c-c5eb-4a3f-bec7-edd1334db900",
    "signature": {
        "$1": "number"
    },
    "results": [
        {
            "$1": 1452770685895835600
        }
    ],
    "status": "success",
    "metrics": {
        "elapsedTime": "1.345504ms",
        "executionTime": "1.299805ms",
        "resultCount": 1,
        "resultSize": 49
    }
}

My cas were in a lower range, hence not impacted:

    328484827496448 vs.
1452770685895835648
#15

Same here

SELECT 145277113373648486;

returns

[
  {
    "$1": 145277113373648480
  }
]

(I’m using the query workbench)

J.

#16

Taking a look, now, thanks.

#17

Hello, do you please have any news of this issue?

J.

#18

I’ve ran into this issue too with 4.1. @geraldss

GET: { cas: CouchbaseCas<236704945078272>, value: .. } N1QL: { cas: 47460610670592 }

The NodeJS SDK is not accepting that cas value of N1QL at all? @brett19 :slight_smile:

#19

N.v.m.
My issue had to do with using Mac OSX version instead of the Mac OSX El Capitan version :slight_smile:

#20

Ran into it again, it seems whenever you create multiple buckets and N1 Query on it (the other buckets besides the default one), it will return an invalid CAS (at least on Mac OS X). I’ve experienced this problem now with 4.0 and 4.1. Any help on this would be appreciated.