package org.swisspush.reststorage.lua;
import org.junit.Test;
import org.swisspush.reststorage.util.LockMode;
import java.util.ArrayList;
import static com.jayway.awaitility.Awaitility.await;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class RedisDelLuaScriptTests extends AbstractLuaScriptTest {
private final static String RESOURCE = "resource";
private static final double MAX_EXPIRE_IN_MILLIS = 9999999999999d;
@Test
public void deleteResource2BranchesDeleteOnRootNode() {
// ARRANGE
evalScriptPut(":project:server:test:test1:test2", "{\"content\": \"test/test1/test2\"}");
evalScriptPut(":project:server:test:test11:test22", "{\"content\": \"test/test1/test2\"}");
// ACT
evalScriptDel(":project");
// ASSERT
assertThat(jedis.zrangeByScore("rest-storage:collections:project", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test1", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test11:test2"), equalTo(false));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test11", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test11:test22"), equalTo(false));
}
@Test
public void deleteResource2BranchesDeleteOnForkNode() {
// ARRANGE
evalScriptPut(":project:server:test:test1:test2", "{\"content\": \"test/test1/test2\"}");
evalScriptPut(":project:server:test:test11:test22", "{\"content\": \"test/test1/test2\"}");
// ACT
evalScriptDel(":project:server:test");
// ASSERT
assertThat(jedis.zrangeByScore("rest-storage:collections:project", System.currentTimeMillis(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test1", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test11:test2"), equalTo(false));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test11", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test11:test22"), equalTo(false));
}
@Test
public void deleteResource2BranchesDeleteOneLevelAboveBranch() {
// ARRANGE
evalScriptPut(":project:server:test:test1:test2", "{\"content\": \"test/test1/test2\"}");
evalScriptPut(":project:server:test:test11:test22", "{\"content\": \"test/test1/test2\"}");
// ACT
evalScriptDel(":project:server:test:test1");
// ASSERT
assertThat(jedis.zrangeByScore("rest-storage:collections:project", getNowAsDouble(), 9999999999999d).iterator().next(), equalTo("server"));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server", getNowAsDouble(), 9999999999999d).iterator().next(), equalTo("test"));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test", getNowAsDouble(), 9999999999999d).iterator().next(), equalTo("test11"));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test11", getNowAsDouble(), 9999999999999d).iterator().next(), equalTo("test22"));
assertThat(jedis.hget("rest-storage:resources:project:server:test:test11:test22", RESOURCE), equalTo("{\"content\": \"test/test1/test2\"}"));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test1", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test1:test2"), equalTo(false));
}
@Test
public void deleteResource2BranchesDeleteOnOneResource() {
// ARRANGE
evalScriptPut(":project:server:test:test1:test2", "{\"content\": \"test/test1/test2\"}");
evalScriptPut(":project:server:test:test11:test22", "{\"content\": \"test/test1/test2\"}");
// ACT
evalScriptDel(":project:server:test:test1:test2");
// ASSERT
assertThat(jedis.zrangeByScore("rest-storage:collections:project", getNowAsDouble(), 9999999999999d).iterator().next(), equalTo("server"));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server", getNowAsDouble(), 9999999999999d).iterator().next(), equalTo("test"));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test", getNowAsDouble(), 9999999999999d).iterator().next(), equalTo("test11"));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test11", getNowAsDouble(), 9999999999999d).iterator().next(), equalTo("test22"));
assertThat(jedis.hget("rest-storage:resources:project:server:test:test11:test22", RESOURCE), equalTo("{\"content\": \"test/test1/test2\"}"));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test1", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test11:test2"), equalTo(false));
}
@Test
public void deleteResource2BranchesDeleteOnBothResources() {
// ARRANGE
evalScriptPut(":project:server:test:test1:test2", "{\"content\": \"test/test1/test2\"}");
evalScriptPut(":project:server:test:test11:test22", "{\"content\": \"test/test1/test2\"}");
// ACT
evalScriptDel(":project:server:test:test1:test2");
evalScriptDel(":project:server:test:test11:test22");
// ASSERT
assertThat(jedis.zrangeByScore("rest-storage:collections:project", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test1", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test1:test2"), equalTo(false));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test11", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test11:test22"), equalTo(false));
}
@Test
public void deleteExpiredResourceWithMaxScoreAtMax() throws InterruptedException {
// ARRANGE
evalScriptPut(":project:server:test:test1:test2", "{\"content\": \"test/test1/test2\"}", "9999999999999");
Thread.sleep(10);
// ACT
evalScriptDel(":project:server:test:test1:test2");
// ASSERT
assertThat(jedis.zrangeByScore("rest-storage:collections:project", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test1", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test1:test2"), equalTo(false));
String valueGet = (String) evalScriptGet(":project:server:test:test1:test2", getNowAsString());
assertThat(valueGet, equalTo("notFound"));
}
@Test
public void deleteExpiredResourceWithMaxScoreNow() throws InterruptedException {
// ARRANGE
String now = String.valueOf(System.currentTimeMillis());
evalScriptPut(":project:server:test:test1:test2", "{\"content\": \"test/test1/test2\"}", now);
Thread.sleep(10);
// ACT
long after = System.currentTimeMillis();
evalScriptDel(":project:server:test:test1:test2", after);
// ASSERT
assertThat(jedis.zrangeByScore("rest-storage:collections:project", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test1", getNowAsDouble(), 9999999999999d).size(), equalTo(0));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test1:test2"), equalTo(true));
String valueGet = (String) evalScriptGet(":project:server:test:test1:test2", getNowAsString());
assertThat(valueGet, equalTo("notFound"));
}
@Test
public void deleteExpiredResourceOfTwo() throws InterruptedException {
// ARRANGE
String now = String.valueOf(System.currentTimeMillis());
String nowPlus1000sec = String.valueOf((System.currentTimeMillis() + 1000000));
evalScriptPut(":project:server:test:test1:test2", "{\"content\": \"test/test1/test2\"}", now);
evalScriptPut(":project:server:test:test11:test22", "{\"content\": \"test/test1/test2\"}", nowPlus1000sec);
Thread.sleep(10);
// ACT
long after = System.currentTimeMillis();
evalScriptDel(":project:server:test:test1:test2", after);
// ASSERT
String afterNow = String.valueOf(System.currentTimeMillis());
assertThat(jedis.zrangeByScore("rest-storage:collections:project", Double.valueOf(afterNow).doubleValue(), 9999999999999d).size(), equalTo(1));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server", Double.valueOf(afterNow).doubleValue(), 9999999999999d).size(), equalTo(1));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test", Double.valueOf(afterNow).doubleValue(), 9999999999999d).size(), equalTo(1));
assertThat(jedis.zrangeByScore("rest-storage:collections:project:server:test:test11", Double.valueOf(afterNow).doubleValue(), 9999999999999d).size(), equalTo(1));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test11:test22"), equalTo(true));
}
@Test
public void deleteNotExpiredResourceOfTwo() throws InterruptedException {
// ARRANGE
String now = String.valueOf(System.currentTimeMillis());
String nowPlus1000sec = String.valueOf((System.currentTimeMillis() + 1000000));
evalScriptPut(":project:server:test:test1:test2", "{\"content\": \"test/test1/test2\"}", now);
evalScriptPut(":project:server:test:test11:test22", "{\"content\": \"test/test1/test2\"}", nowPlus1000sec);
Thread.sleep(10);
// ACT
long after = System.currentTimeMillis();
evalScriptDel(":project:server:test:test11:test22", after);
// ASSERT
assertThat(jedis.exists("rest-storage:collections:project"), equalTo(true));
assertThat(jedis.exists("rest-storage:collections:project:server"), equalTo(true));
assertThat(jedis.exists("rest-storage:collections:project:server:test"), equalTo(true));
assertThat(jedis.exists("rest-storage:collections:project:server:test:test11"), equalTo(false));
assertThat(jedis.exists("rest-storage:resources:project:server:test:test11:test22"), equalTo(false));
}
@Test
public void deleteSilentLockedResourceWithoutOwnership() throws InterruptedException {
// ARRANGE
String path = ":project:lock:delete:";
String content = "{\"content\":\"locked\"}";
// ACT - 1 (without any owner)
evalScriptPut(path, content, MAX_EXPIRE, "etag", "test", LockMode.SILENT, 10);
String value = evalScriptDel(path, "", LockMode.SILENT, 10);
// ASSERT - 1
assertThat(value, equalTo("silent"));
assertThat(jedis.exists(prefixLock + path), equalTo(true));
// ACT - 2 (with different owner)
value = evalScriptDel(path, "test2", LockMode.SILENT, 10);
// ASSERT - 2
assertThat(value, equalTo("silent"));
assertThat(jedis.exists(prefixLock + path), equalTo(true));
}
@Test
public void deleteSilentLockedResourceWithOwnership() throws InterruptedException {
// ARRANGE
String path = ":project:lock:delete:";
String content = "{\"content\":\"locked\"}";
// ACT
evalScriptPut(path, content, MAX_EXPIRE, "etag", "test", LockMode.SILENT, 10);
assertThat(jedis.exists("rest-storage:resources" + path), equalTo(true));
String value = evalScriptDel(path, "test", LockMode.SILENT, 10);
// ASSERT
assertThat(value, equalTo("deleted"));
assertThat(jedis.exists(prefixLock + path), equalTo(true));
assertThat(jedis.exists("rest-storage:resources" + path), equalTo(false));
}
@Test
public void deleteRejectLockedResourceWithoutOwnership() {
// ARRANGE
String path = ":project:lock:delete:";
String content = "{\"content\":\"locked\"}";
// ACT - 1 (without any owner)
evalScriptPut(path, content, MAX_EXPIRE, "etag", "test", LockMode.REJECT, 10);
String value = evalScriptDel(path, "", LockMode.REJECT, 10);
// ASSERT - 1
assertThat(value, equalTo("reject"));
assertThat(jedis.exists(prefixLock + path), equalTo(true));
// ACT - 2 (with different owner)
value = evalScriptDel(path, "test2", LockMode.REJECT, 10);
// ASSERT - 2
assertThat(value, equalTo("reject"));
assertThat(jedis.exists(prefixLock + path), equalTo(true));
}
@Test
public void deleteRejectLockedResourceWithOwnership() throws InterruptedException {
// ARRANGE
String path = ":project:lock:delete:";
String content = "{\"content\":\"locked\"}";
// ACT
evalScriptPut(path, content, MAX_EXPIRE, "etag", "test", LockMode.REJECT, 10);
assertThat(jedis.exists("rest-storage:resources" + path), equalTo(true));
String value = evalScriptDel(path, "test", LockMode.REJECT, 10);
// ASSERT
assertThat(value, equalTo("deleted"));
assertThat(jedis.exists(prefixLock + path), equalTo(true));
assertThat(jedis.exists("rest-storage:resources" + path), equalTo(false));
}
@Test
public void deleteCollectionWithLockedRessource() {
// ARRANGE
String base = ":project:lock:delete:collection";
String path1 = base + ":res1";
String path2 = base + ":res2";
String path3 = base + ":res3";
String content1 = "{\"content\":\"1\"}";
String content2 = "{\"content\":\"locked\"}";
String content3 = "{\"content\":\"3\"}";
// ACT
evalScriptPut(path1, content1, MAX_EXPIRE);
evalScriptPut(path2, content2, MAX_EXPIRE, "etag", "test", LockMode.REJECT, 10);
evalScriptPut(path3, content3, MAX_EXPIRE);
assertThat(jedis.exists("rest-storage:resources" + path1), equalTo(true));
assertThat(jedis.exists("rest-storage:resources" + path2), equalTo(true));
assertThat(jedis.exists("rest-storage:resources" + path3), equalTo(true));
String value = (String) evalScriptDel(base);
// ASSERT
assertThat(value, equalTo("deleted"));
assertThat(jedis.exists(prefixLock + path2), equalTo(false));
assertThat(jedis.exists("rest-storage:resources" + path1), equalTo(false));
assertThat(jedis.exists("rest-storage:resources" + path2), equalTo(false));
assertThat(jedis.exists("rest-storage:resources" + path3), equalTo(false));
}
@Test
public void tryToDeleteWhileAndAfterResourceLock() {
// ARRANGE
long lockExpire = 5;
String path = ":project:lock:delete:";
String content = "{\"content\":\"locked\"}";
// ACT
jedis.expire(prefixLock + path, 0);
String lockedValue = evalScriptPut(path, content, AbstractLuaScriptTest.MAX_EXPIRE, "etag", "owner1", LockMode.SILENT, lockExpire);
assertThat(lockedValue, is(equalTo("OK")));
assertThat(jedis.hget("rest-storage:resources" + path, RESOURCE), equalTo(content));
// ASSERT
await().atMost(lockExpire * 2, SECONDS).until(() ->
evalScriptDel(path, "", LockMode.SILENT, 10),
equalTo("deleted")
);
assertThat(jedis.exists(prefixLock + path), equalTo(false));
}
@SuppressWarnings({ "rawtypes", "unchecked", "serial" })
private String evalScriptDel(final String resourceName, final String lockOwner, LockMode lockMode, long lockExpire) {
String delScript = readScript("del.lua");
String lockExpireInMillis = String.valueOf(System.currentTimeMillis() + (lockExpire * 1000));
return (String) jedis.eval(delScript, new ArrayList() {
{
add(resourceName);
}
},
new ArrayList() {
{
add(prefixResources);
add(prefixCollections);
add(prefixDeltaResources);
add(prefixDeltaEtags);
add(expirableSet);
add("0");
add("9999999999999");
add(prefixLock);
add(lockOwner);
add(lockMode.text());
add(lockExpireInMillis);
}
}
);
}
@SuppressWarnings({ "rawtypes", "unchecked", "serial" })
private Object evalScriptDel(final String resourceName) {
String putScript = readScript("del.lua");
return jedis.eval(putScript, new ArrayList() {
{
add(resourceName);
}
},
new ArrayList() {
{
add(prefixResources);
add(prefixCollections);
add(prefixDeltaResources);
add(prefixDeltaEtags);
add(expirableSet);
add("0");
add("9999999999999");
add(prefixLock);
}
}
);
}
@SuppressWarnings({ "rawtypes", "unchecked", "serial" })
private Object evalScriptDel(final String resourceName, final long maxscore) {
String delScript = readScript("del.lua");
return jedis.eval(delScript, new ArrayList() {
{
add(resourceName);
}
}, new ArrayList() {
{
add(prefixResources);
add(prefixCollections);
add(prefixDeltaResources);
add(prefixDeltaEtags);
add(expirableSet);
add(getNowAsString());
add(String.valueOf(MAX_EXPIRE_IN_MILLIS));
add(prefixLock);
}
}
);
}
}