RecordExistsAction.CREATE_ONLY and SendKey isnt working on Java Object Mapper

Hello,

We are using Java Object Mapper to save and read the records from the Database. We have Configured our write policy to make sure we store the key in DB by using writePolicy.sendKey = true; and RecordExistsAction x=RecordExistsAction.CREATE_ONLY; to make sure we dont insert duplicate records.

But these configurations are not working when we run the application , i see the data _KEY Bin was Null every time we insert it and the record gets updated on every save .

Below is the write policy and sample DB record

RecordExistsAction x=RecordExistsAction.CREATE_ONLY;
	writePolicy.sendKey = true;
	writePolicy.expiration = 300;
	writePolicy.recordExistsAction=x;
	policy.writePolicyDefault = writePolicy;

You have an expiration of 300 seconds, so I assume you set the updates within less than 300 seconds?

@pgupta , thanks for the reply .Yes that’s true , but even if the expiration parameter is removed , I see the same behavior

Not sure what is the issue on your side but works for me. Here is the code snippet I ran, starting with the record not present on the cluster:

AerospikeClient client = new AerospikeClient("localhost", 3000);
System.out.println("Initialized the client and connected to the cluster.");
Key key = new Key("test", "testset", "key1");
WritePolicy wP = new WritePolicy();
wP.sendKey = true;
wP.expiration = 300;
wP.recordExistsAction = RecordExistsAction.CREATE_ONLY;
Bin bin1 = new Bin("age", 40);
try {
  client.put(wP, key, bin1);
} catch(Exception e) {
    System.out.printf(e.toString());
}
 
Record record = client.get(null, key);
System.out.print("\nRecord:");
System.out.println(record);

System.out.println("\nRe-insert the record:");
//This should fail for CREATE_ONLY.  We are updating again within ~milliseconds.
try {
  client.put(wP, key, bin1);
} catch(Exception e) {
    System.out.println(e.toString());
}

Record record = client.get(null, key);
System.out.print("\nRecord:");
System.out.println(record);

and the output is:

Initialized the client and connected to the cluster.

Record:(gen:1),(exp:453573774),(bins:(age:40))

Re-insert the record:
com.aerospike.client.AerospikeException: Error 5,1,0,30000,1000,0,A1 127.0.0.1 3000: Key already exists

Record:(gen:1),(exp:453573774),(bins:(age:40))

Regarding not getting your key back, when you read a record in a single read transaction - you already know your key - so server does not send it back. The Record object does not have the key field. No logical reason to have it.

However, if you were to run a Secondary Index query and return a RecordSet - then server returns the key of each record if you stored it with sendKey true. So if I run an SI query on this one record that I just stored, I can get the key. See code below:

//Run SI query
Statement stmt = new Statement();
stmt.setNamespace("test");
stmt.setSetName("testset");
stmt.setFilter(Filter.range("age", 20,50));
QueryPolicy qp = new QueryPolicy();

RecordSet rs = client.query(qp, stmt);
while (rs.next()){
  System.out.println(rs.getKey());
  Record r = rs.getRecord();
  System.out.println(r);
}

and the output is:

test:testset:key1:bf6c1d13e7cd10c5bd022d27e7df170c0bccd6e1
(gen:1),(exp:453573774),(bins:(age:40))

The key, when you send it, is not stored in a record bin per se. Can’t think of it like a bin value. You can explicitly store it in a bin as part of the record data, if you so wish, from the application side.

Hi @pgupta , if we use client this will work , but we are using Java object mapper to save and to get the record

I have not played with the Java Object Mapper - so I will request someone else internally who knows it very well, to assist you.

With that disclaimer, the Object Mapper is a middle layer between your application and the server. If the server supports a functionality, the Object Mapper must support too, especially something so fundamental like this - Write Policy. There may be something in the usage that is missing. I will reach out to our J-O-M expert.

Hi @vamsi.koneru1

Thank you for raising this. I’ve done some investigation, and this is a genuine bug. The problem is that the save is really trying to do a REPLACE action to remove any fields which exist in the record but are not part of the object being saved. However, it doesn’t cater for code like yours is trying to set a different RecordExistsAction on the save call as the REPLACE action is set pretty aggressively on the policy. I will need to think about the best way to handle this case.

This is a workaround until this is solved properly in the code. You can change your call from:

mapper.save(myObject);

to

mapper.save(mapper.getWritePolicy(MyObject.class), myObject);

This will force it to use the policy in the policy you define without overriding the RecordExistsAction. This is not a great solution, but it will work for now until this is fixed properly.