Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
87 changes: 87 additions & 0 deletions src/main/java/net/spy/memcached/v2/AsyncArcusCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
import net.spy.memcached.ops.CollectionGetOperation;
import net.spy.memcached.ops.CollectionInsertOperation;
import net.spy.memcached.ops.ConcatenationType;
import net.spy.memcached.ops.GetAttrOperation;
import net.spy.memcached.ops.GetOperation;
import net.spy.memcached.ops.GetsOperation;
import net.spy.memcached.ops.Mutator;
Expand All @@ -103,6 +104,8 @@
import net.spy.memcached.ops.StoreType;
import net.spy.memcached.transcoders.Transcoder;
import net.spy.memcached.transcoders.TranscoderUtils;
import net.spy.memcached.v2.attribute.ItemAttributes;
import net.spy.memcached.v2.attribute.UpdateAttributes;
import net.spy.memcached.v2.vo.BKey;
import net.spy.memcached.v2.vo.BTreeElement;
import net.spy.memcached.v2.vo.BTreeGetResult;
Expand Down Expand Up @@ -2382,4 +2385,88 @@ public void complete() {
return resultMap;
});
}

@Override
public ArcusFuture<Boolean> setAttributes(String key, UpdateAttributes attributes) {
AbstractArcusResult<Boolean> result = new AbstractArcusResult<>(new AtomicReference<>());
ArcusFutureImpl<Boolean> future = new ArcusFutureImpl<>(result);
ArcusClient client = arcusClientSupplier.get();

OperationCallback cb = new OperationCallback() {
@Override
public void receivedStatus(OperationStatus status) {
switch (status.getStatusCode()) {
case SUCCESS:
result.set(true);
break;
case ERR_NOT_FOUND:
result.set(false);
break;
case CANCELLED:
future.internalCancel();
break;
default:
/*
* ATTR_ERROR or unknown statement.
*/
result.addError(key, status);
}
}

@Override
public void complete() {
future.complete();
}
};
Operation op = client.getOpFact().setAttr(key, attributes, cb);
future.setOp(op);
client.addOp(key, op);

return future;
}

@Override
public ArcusFuture<ItemAttributes> getAttributes(String key) {
AbstractArcusResult<ItemAttributes> result = new AbstractArcusResult<>(new AtomicReference<>());
ArcusFutureImpl<ItemAttributes> future = new ArcusFutureImpl<>(result);
ArcusClient client = arcusClientSupplier.get();

ItemAttributes attributes = new ItemAttributes();
GetAttrOperation.Callback cb = new GetAttrOperation.Callback() {
@Override
public void gotAttribute(String key, String attr) {
attributes.setAttribute(attr);
}

@Override
public void receivedStatus(OperationStatus status) {
switch (status.getStatusCode()) {
case SUCCESS:
result.set(attributes);
break;
case ERR_NOT_FOUND:
result.set(null);
break;
case CANCELLED:
future.internalCancel();
break;
default:
/*
* ATTR_ERROR or unknown statement.
*/
result.addError(key, status);
}
}

@Override
public void complete() {
future.complete();
}
};
Operation op = client.getOpFact().getAttr(key, cb);
future.setOp(op);
client.addOp(key, op);

return future;
}
}
19 changes: 19 additions & 0 deletions src/main/java/net/spy/memcached/v2/AsyncArcusCommandsIF.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import net.spy.memcached.collection.CreateAttributes;
import net.spy.memcached.collection.ElementFlagFilter;
import net.spy.memcached.collection.ElementValueType;
import net.spy.memcached.v2.attribute.ItemAttributes;
import net.spy.memcached.v2.attribute.UpdateAttributes;
import net.spy.memcached.v2.vo.BKey;
import net.spy.memcached.v2.vo.BTreeElement;
import net.spy.memcached.v2.vo.BTreeGetResult;
Expand Down Expand Up @@ -885,4 +887,21 @@ ArcusFuture<Boolean> bopDelete(String key, BKey from, BKey to,
* @return a map of each server's {@link java.net.SocketAddress} to its version string
*/
ArcusFuture<Map<SocketAddress, String>> versions();

/**
* Set the attributes of the item stored at the given key.
*
* @param key the key
* @param attributes the attributes to set
* @return {@code true} if the attributes were set, otherwise {@code false}
*/
ArcusFuture<Boolean> setAttributes(String key, UpdateAttributes attributes);

/**
* Get the attributes of the item stored at the given key.
*
* @param key the key
* @return the {@link ItemAttributes} of the item, or {@code null} if not found
*/
ArcusFuture<ItemAttributes> getAttributes(String key);
}
25 changes: 22 additions & 3 deletions src/main/java/net/spy/memcached/v2/attribute/UpdateAttributes.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ private UpdateAttributes(Builder builder) {
b.append(" overflowaction=").append(builder.overflowAction);
}
if (builder.readable != null) {
b.append(" readable=").append(builder.readable ? "on" : "off");
b.append(" readable=").append("on");
}
if (builder.maxBKeyRange != null) {
b.append(" maxbkeyrange=").append(builder.maxBKeyRange);
Expand Down Expand Up @@ -66,6 +66,25 @@ public Builder expireTime(long expireTime) {
return this;
}

/**
* Sets the maximum number of elements the collection may hold.
*
* <p>The value is interpreted by the server, and two cases are handled
* specially:
* <ul>
* <li>
* If the value is {@code 0}, the server applies its default count (4,000).
* </li>
* <li>
* If the value is greater than the server's configured maximum,
* the server silently clamps it to that maximum
* and still reports the operation as successful.
* </li>
* </ul>
*
* @param maxCount the maximum number of elements
* @return this Builder
*/
public Builder maxCount(long maxCount) {
this.maxCount = maxCount;
return this;
Expand All @@ -76,8 +95,8 @@ public Builder overflowAction(CollectionOverflowAction overflowAction) {
return this;
}

public Builder readable(boolean readable) {
this.readable = readable;
public Builder readable() {
this.readable = true;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package net.spy.memcached.v2;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import net.spy.memcached.collection.CollectionOverflowAction;
import net.spy.memcached.collection.CreateAttributes;
import net.spy.memcached.collection.ElementValueType;
import net.spy.memcached.ops.OperationException;
import net.spy.memcached.v2.attribute.UpdateAttributes;
import net.spy.memcached.v2.vo.BKey;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class AttributesAsyncArcusCommandsTest extends AsyncArcusCommandsTest {

@Test
void setKVAttributesSuccess() throws ExecutionException, InterruptedException, TimeoutException {
// given
String key = keys.get(0);

async.set(key, 0, VALUE)
.thenAccept(Assertions::assertTrue)
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);

// when
UpdateAttributes attributes = UpdateAttributes.builder()
.expireTime(100L)
.build();

async.setAttributes(key, attributes)
// then
.thenAccept(Assertions::assertTrue)
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);
}

@Test
void setBTreeAttributesSuccess()
throws ExecutionException, InterruptedException, TimeoutException {
// given
String key = keys.get(0);

CreateAttributes createAttributes = CreateAttributes.builder()
.readable(false)
.build();

async.bopCreate(key, ElementValueType.STRING, createAttributes)
.thenAccept(Assertions::assertTrue)
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);

// when
UpdateAttributes attributes = UpdateAttributes.builder()
.expireTime(100L)
.maxCount(5_000L)
.overflowAction(CollectionOverflowAction.largest_trim)
.maxBKeyRange(BKey.of(10L))
.readable()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

readable = true 설정 경우만 있어서, 인자를 제외한 것이네요.

collection create 하는 경우는 unreadble 설정만 가능하지만,
readable()에서 true/false 값을 받고 있습니다. 참고 바랍니다.

.build();

async.setAttributes(key, attributes)
.thenCompose(result -> {
assertTrue(result);
return async.getAttributes(key);
})
.thenAccept(result -> {
assertNotNull(result);
assertEquals(100L, result.getExpireTime());
assertEquals(5_000L, result.getMaxCount());
assertEquals(CollectionOverflowAction.largest_trim, result.getOverflowAction());
assertEquals(true, result.getReadable());
})
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);
}

@Test
void setAttributesFailureKeyNotFound()
throws ExecutionException, InterruptedException, TimeoutException {
// given
String key = keys.get(0);

UpdateAttributes attributes = UpdateAttributes.builder()
.expireTime(100L)
.build();

// when
async.setAttributes(key, attributes)
// then
.thenAccept(Assertions::assertFalse)
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);
}

@Test
void setAttributesFailureMapOverflowAction()
throws ExecutionException, InterruptedException, TimeoutException {
// given
String key = keys.get(0);

async.mopCreate(key, ElementValueType.STRING, CreateAttributes.DEFAULT)
.thenAccept(Assertions::assertTrue)
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);

UpdateAttributes attributes = UpdateAttributes.builder()
.overflowAction(CollectionOverflowAction.smallest_trim)
.build();

// when
async.setAttributes(key, attributes)
// then
.handle((result, ex) -> {
assertInstanceOf(OperationException.class, ex);
assertTrue(ex.getMessage().contains("ATTR_ERROR"));
return result;
})
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);
}

@Test
void setAttributesFailureSetOverflowAction()
throws ExecutionException, InterruptedException, TimeoutException {
// given
String key = keys.get(0);

async.sopCreate(key, ElementValueType.STRING, CreateAttributes.DEFAULT)
.thenAccept(Assertions::assertTrue)
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);

UpdateAttributes attributes = UpdateAttributes.builder()
.overflowAction(CollectionOverflowAction.largest_trim)
.build();

// when
async.setAttributes(key, attributes)
// then
.handle((result, ex) -> {
assertInstanceOf(OperationException.class, ex);
assertTrue(ex.getMessage().contains("ATTR_ERROR"));
return result;
})
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);
}

@Test
void getAttributesSuccess() throws ExecutionException, InterruptedException, TimeoutException {
// given
String key = keys.get(0);

CreateAttributes attributes = CreateAttributes.builder()
.expireTime(100L)
.maxCount(5000L)
.overflowAction(CollectionOverflowAction.smallest_trim)
.build();

async.bopCreate(key, ElementValueType.STRING, attributes)
.thenAccept(Assertions::assertTrue)
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);

// when
async.getAttributes(key)
// then
.thenAccept(result -> {
assertNotNull(result);
assertTrue(result.getExpireTime() <= 100L);
assertEquals(5_000L, result.getMaxCount());
assertEquals(CollectionOverflowAction.smallest_trim, result.getOverflowAction());
})
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);
}

@Test
void getAttributesFailureKeyNotFound()
throws ExecutionException, InterruptedException, TimeoutException {
// given
String key = keys.get(0);

// when
async.getAttributes(key)
// then
.thenAccept(Assertions::assertNull)
.toCompletableFuture()
.get(300L, TimeUnit.MILLISECONDS);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void multipleFieldsJoinedBySpace() {
UpdateAttributes attributes = UpdateAttributes.builder()
.maxCount(2_000L)
.overflowAction(CollectionOverflowAction.error)
.readable(true)
.readable()
.build();

assertEquals("maxcount=2000 overflowaction=error readable=on", attributes.stringify());
Expand Down
Loading