Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.aerospike.mapper.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Mark a field or property to be used for optimistic concurrency control using Aerospike's generation field.
* <p/>
* The field or property must be of Integer or int type. When reading an object which has a field marked
* with @Version, the returned record's generation field will be mapped into the @Version field.
* When writing the record, if the @Version field is non-zero, the generation will be set in the
* writePolicy.generation field and the writePolicy.generationPolicy will be set to
* GenerationPolicy.EXPECT_GEN_EQUAL.
* <p/>
* Example usage:
* <pre>
* &#64;AerospikeRecord(namespace = "test", set = "account")
* public class Account {
* &#64;AerospikeKey
* private int id;
* &#64;AerospikeBin
* private String name;
* &#64;Version
* private int version;
* }
* </pre>
*
* @author timfaulkes
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface AerospikeGeneration {
}
9 changes: 9 additions & 0 deletions src/main/java/com/aerospike/mapper/tools/AeroMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.aerospike.client.Record;
import com.aerospike.client.Value;
import com.aerospike.client.policy.BatchPolicy;
import com.aerospike.client.policy.GenerationPolicy;
import com.aerospike.client.policy.Policy;
import com.aerospike.client.policy.QueryPolicy;
import com.aerospike.client.policy.RecordExistsAction;
Expand Down Expand Up @@ -121,6 +122,14 @@ private <T> WritePolicy generateWritePolicyFromObject(T object) {
if (sendKey != null) {
writePolicy.sendKey = sendKey;
}

// #181 Handle @Version field for optimistic concurrency control
Integer versionValue = entry.getGenerationValue(object);
if (versionValue != null && versionValue > 0) {
writePolicy.generation = versionValue;
writePolicy.generationPolicy = GenerationPolicy.EXPECT_GEN_EQUAL;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If for some reason, the class's write policy is set to GenerationPolicy.EXPECT_GEN_GT will we be overriding it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I have never found any reason to use EXPECT_GEN_GT, it makes no sense as generations wrap around.

Good catches on those couple of trailing references to version instead of generation!

}

return writePolicy;
}

Expand Down
476 changes: 320 additions & 156 deletions src/main/java/com/aerospike/mapper/tools/ClassCacheEntry.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.aerospike.client.Operation;
import com.aerospike.client.Value;
import com.aerospike.client.policy.BatchPolicy;
import com.aerospike.client.policy.GenerationPolicy;
import com.aerospike.client.policy.Policy;
import com.aerospike.client.policy.QueryPolicy;
import com.aerospike.client.policy.RecordExistsAction;
Expand Down Expand Up @@ -107,6 +108,14 @@ private <T> WritePolicy generateWritePolicyFromObject(T object) {
if (sendKey != null) {
writePolicy.sendKey = sendKey;
}

// #181 Handle @Version field for optimistic concurrency control
Integer generationValue = entry.getGenerationValue(object);
if (generationValue != null && generationValue > 0) {
writePolicy.generation = generationValue;
writePolicy.generationPolicy = GenerationPolicy.EXPECT_GEN_EQUAL;
}

return writePolicy;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class BinConfig {
private Integer ordinal;
private EmbedConfig embed;
private ReferenceConfig reference;
private Boolean generation;

public String getName() {
return name;
Expand Down Expand Up @@ -51,7 +52,10 @@ public ReferenceConfig getReference() {
return reference;
}


public Boolean isGeneration() {
return generation;
}

public void setName(String name) {
this.name = name;
}
Expand Down Expand Up @@ -88,6 +92,10 @@ public void setReference(ReferenceConfig reference) {
this.reference = reference;
}

public void setGeneration(Boolean version) {
this.generation = version;
}

public void validate(String className) {
if (StringUtils.isBlank(this.name) && StringUtils.isBlank(this.field)) {
throw new AerospikeException("Configuration for class " + className + " defines a bin which contains neither a name nor a field");
Expand Down Expand Up @@ -129,6 +137,9 @@ public BinConfig merge(BinConfig other) {
if (this.reference == null && other.reference != null) {
this.reference = other.reference;
}
if (this.generation == null && other.generation != null) {
this.generation = other.generation;
}
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class ClassConfig {
private String namespace;
private String set;
private Integer ttl;
private Integer version;
private Integer generation;
private Boolean sendKey;
private Boolean mapAll;
private Boolean durableDelete;
Expand Down Expand Up @@ -48,7 +48,7 @@ public Integer getTtl() {
}

public Integer getVersion() {
return version;
return generation;
}

public Boolean getSendKey() {
Expand Down Expand Up @@ -140,8 +140,8 @@ private void setTtl(Integer ttl) {
this.ttl = ttl;
}

private void setVersion(Integer version) {
this.version = version;
private void setGeneration(Integer generation) {
this.generation = generation;
}

private void setSendKey(Boolean sendKey) {
Expand Down Expand Up @@ -199,8 +199,8 @@ public Builder withTtl(int ttl) {
return this;
}

public Builder withVersion(int version) {
this.classConfig.setVersion(version);
public Builder withGeneration(int generation) {
this.classConfig.setGeneration(generation);
return this;
}

Expand Down Expand Up @@ -321,6 +321,11 @@ public Builder beingEmbeddedAs(AerospikeEmbed.EmbedType type, AerospikeEmbed.Emb
return this.end();
}

public Builder asGenerationField() {
this.binConfig.setGeneration(true);
return this.end();
}

public Builder beingEmbeddedAs(AerospikeEmbed.EmbedType type, AerospikeEmbed.EmbedType elementType, boolean saveKey) {
EmbedConfig embedConfig = new EmbedConfig();
embedConfig.setType(type);
Expand Down
4 changes: 3 additions & 1 deletion src/test/java/com/aerospike/mapper/AeroMapperBaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.aerospike.client.policy.ClientPolicy;

import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Host;
import com.aerospike.client.IAerospikeClient;
import com.aerospike.mapper.tools.ClassCache;
import com.fasterxml.jackson.core.JsonProcessingException;
Expand All @@ -25,7 +26,8 @@ public static void setupClass() {
ClientPolicy policy = new ClientPolicy();
// Set event loops to use in asynchronous commands.
policy.eventLoops = new NioEventLoops(1);
client = new AerospikeClient(policy, "localhost", 3000);
Host[] hosts = Host.parseHosts(System.getProperty("test.host", "localhost:3000"), 3000);
client = new AerospikeClient(policy, hosts);
}

@AfterAll
Expand Down
Loading
Loading