DATAREDIS-744 - Support Redis hashes with colon in their id.

We now support Redis hashes via Repository support that contain colon in their id. We're using colons to split a composite id string into keyspace and id parts. Previously, we partially rejected processing of id's that don't exactly match the number of parts delimited by colon. This caused leftovers in secondary indexes.

Original Pull Request: #298
This commit is contained in:
Mark Paluch
2017-12-19 10:57:50 +01:00
committed by Christoph Strobl
parent 323cb32861
commit 5de73903c1
7 changed files with 434 additions and 43 deletions

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* Unit tests for {@link RedisKeyExpiredEvent}.
*
* @author Mark Paluch
*/
public class RedisKeyExpiredEventUnitTests {
@Test // DATAREDIS-744
public void shouldReturnKeyspace() {
assertThat(new RedisKeyExpiredEvent<Object>("foo".getBytes(), "").getKeyspace(), is(nullValue()));
assertThat(new RedisKeyExpiredEvent<Object>("foo:bar".getBytes(), "").getKeyspace(), is(equalTo("foo")));
assertThat(new RedisKeyExpiredEvent<Object>("foo:bar:baz".getBytes(), "").getKeyspace(), is(equalTo("foo")));
}
@Test // DATAREDIS-744
public void shouldReturnId() {
assertThat(new RedisKeyExpiredEvent<Object>("foo".getBytes(), "").getId(), is(equalTo("foo".getBytes())));
assertThat(new RedisKeyExpiredEvent<Object>("foo:bar".getBytes(), "").getId(), is(equalTo("bar".getBytes())));
assertThat(new RedisKeyExpiredEvent<Object>("foo:bar:baz".getBytes(), "").getId(),
is(equalTo("bar:baz".getBytes())));
}
}

View File

@@ -146,6 +146,20 @@ public class RedisKeyValueAdapterTests {
assertThat(template.opsForHash().entries("persons:1").size(), is(2));
}
@Test // DATAREDIS-744
public void putWritesDataWithColonCorrectly() {
Person rand = new Person();
rand.age = 24;
adapter.put("1:a", rand, "persons");
assertThat(template.keys("persons*"), hasItems("persons", "persons:1:a"));
assertThat(template.opsForSet().size("persons"), is(1L));
assertThat(template.opsForSet().members("persons"), hasItems("1:a"));
assertThat(template.opsForHash().entries("persons:1:a").size(), is(2));
}
@Test // DATAREDIS-425
public void putWritesSimpleIndexDataCorrectly() {
@@ -158,6 +172,18 @@ public class RedisKeyValueAdapterTests {
assertThat(template.opsForSet().members("persons:firstname:rand"), hasItems("1"));
}
@Test // DATAREDIS-744
public void putWritesSimpleIndexDataWithColonCorrectly() {
Person rand = new Person();
rand.firstname = "rand";
adapter.put("1:a", rand, "persons");
assertThat(template.keys("persons*"), hasItem("persons:firstname:rand"));
assertThat(template.opsForSet().members("persons:firstname:rand"), hasItems("1:a"));
}
@Test // DATAREDIS-425
public void putWritesNestedDataCorrectly() {
@@ -184,15 +210,15 @@ public class RedisKeyValueAdapterTests {
assertThat(template.opsForSet().members("persons:address.country:Andor"), hasItems("1"));
}
@Test // DATAREDIS-425
@Test // DATAREDIS-425, DATAREDIS-744
public void getShouldReadSimpleObjectCorrectly() {
Map<String, String> map = new LinkedHashMap<>();
map.put("_class", Person.class.getName());
map.put("age", "24");
template.opsForHash().putAll("persons:load-1", map);
template.opsForHash().putAll("persons:load-1:a", map);
Object loaded = adapter.get("load-1", "persons");
Object loaded = adapter.get("load-1:a", "persons");
assertThat(loaded, instanceOf(Person.class));
assertThat(((Person) loaded).age, is(24));
@@ -257,7 +283,7 @@ public class RedisKeyValueAdapterTests {
assertThat(template.opsForSet().members("persons:firstname:rand"), not(hasItem("1")));
}
@Test // DATAREDIS-425
@Test // DATAREDIS-425, DATAREDIS-744
public void keyExpiredEventShouldRemoveHelperStructures() throws Exception {
assumeTrue(RedisTestProfileValueSource.matches("runLongTests", "true"));
@@ -267,22 +293,22 @@ public class RedisKeyValueAdapterTests {
map.put("firstname", "rand");
map.put("address.country", "Andor");
template.opsForHash().putAll("persons:1", map);
template.opsForHash().putAll("persons:1:b", map);
template.opsForSet().add("persons", "1");
template.opsForSet().add("persons:firstname:rand", "1");
template.opsForSet().add("persons:1:idx", "persons:firstname:rand");
template.opsForSet().add("persons:firstname:rand", "1:b");
template.opsForSet().add("persons:1:b:idx", "persons:firstname:rand");
template.expire("persons:1", 100, TimeUnit.MILLISECONDS);
template.expire("persons:1:b", 100, TimeUnit.MILLISECONDS);
waitUntilKeyIsGone(template, "persons:1");
waitUntilKeyIsGone(template, "persons:1:phantom");
waitUntilKeyIsGone(template, "persons:1:b");
waitUntilKeyIsGone(template, "persons:1:b:phantom");
waitUntilKeyIsGone(template, "persons:firstname:rand");
assertThat(template.hasKey("persons:1"), is(false));
assertThat(template.hasKey("persons:firstname:rand"), is(false));
assertThat(template.hasKey("persons:1:idx"), is(false));
assertThat(template.opsForSet().members("persons"), not(hasItem("1")));
assertThat(template.hasKey("persons:1:b:idx"), is(false));
assertThat(template.opsForSet().members("persons"), not(hasItem("1:b")));
}
@Test // DATAREDIS-589
@@ -348,17 +374,17 @@ public class RedisKeyValueAdapterTests {
assertThat(template.opsForSet().isMember("persons:mat:idx", "persons:firstname:mat"), is(true));
}
@Test // DATAREDIS-471
@Test // DATAREDIS-471, DATAREDIS-744
public void updateShouldAlterIndexDataCorrectly() {
Person rand = new Person();
rand.firstname = "rand";
adapter.put("1", rand, "persons");
adapter.put("1:a", rand, "persons");
assertThat(template.hasKey("persons:firstname:rand"), is(true));
PartialUpdate<Person> update = new PartialUpdate<>("1", Person.class) //
PartialUpdate<Person> update = new PartialUpdate<>("1:a", Person.class) //
.set("firstname", "mat");
adapter.update(update);

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core.convert;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.redis.core.convert.MappingRedisConverter.BinaryKeyspaceIdentifier;
/**
* Unit tests for {@link BinaryKeyspaceIdentifier}.
*
* @author Mark Paluch
*/
public class BinaryKeyspaceIdentifierUnitTests {
@Test // DATAREDIS-744
public void shouldReturnIfKeyIsValid() {
assertThat(BinaryKeyspaceIdentifier.isValid(null), is(false));
assertThat(BinaryKeyspaceIdentifier.isValid("foo".getBytes()), is(false));
assertThat(BinaryKeyspaceIdentifier.isValid("".getBytes()), is(false));
assertThat(BinaryKeyspaceIdentifier.isValid("foo:bar".getBytes()), is(true));
assertThat(BinaryKeyspaceIdentifier.isValid("foo:bar:baz".getBytes()), is(true));
assertThat(BinaryKeyspaceIdentifier.isValid("foo:bar:baz:phantom".getBytes()), is(true));
}
@Test // DATAREDIS-744
public void shouldReturnKeyspace() {
assertThat(BinaryKeyspaceIdentifier.of("foo:bar".getBytes()).getKeyspace(), is(equalTo("foo".getBytes())));
assertThat(BinaryKeyspaceIdentifier.of("foo:bar:baz".getBytes()).getKeyspace(), is(equalTo("foo".getBytes())));
assertThat(BinaryKeyspaceIdentifier.of("foo:bar:baz:phantom".getBytes()).getKeyspace(),
is(equalTo("foo".getBytes())));
}
@Test // DATAREDIS-744
public void shouldReturnId() {
assertThat(BinaryKeyspaceIdentifier.of("foo:bar".getBytes()).getId(), is(equalTo("bar".getBytes())));
assertThat(BinaryKeyspaceIdentifier.of("foo:bar:baz".getBytes()).getId(), is(equalTo("bar:baz".getBytes())));
assertThat(BinaryKeyspaceIdentifier.of("foo:bar:baz:phantom".getBytes()).getId(),
is(equalTo("bar:baz".getBytes())));
}
@Test // DATAREDIS-744
public void shouldReturnPhantomKey() {
assertThat(BinaryKeyspaceIdentifier.of("foo:bar".getBytes()).isPhantomKey(), is(false));
assertThat(BinaryKeyspaceIdentifier.of("foo:bar:baz".getBytes()).isPhantomKey(), is(false));
assertThat(BinaryKeyspaceIdentifier.of("foo:bar:baz:phantom".getBytes()).isPhantomKey(), is(true));
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.core.convert;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.redis.core.convert.MappingRedisConverter.KeyspaceIdentifier;
/**
* Unit tests for {@link KeyspaceIdentifier}.
*
* @author Mark Paluch
*/
public class KeyspaceIdentifierUnitTests {
@Test // DATAREDIS-744
public void shouldReturnIfKeyIsValid() {
assertThat(KeyspaceIdentifier.isValid(null), is(false));
assertThat(KeyspaceIdentifier.isValid("foo"), is(false));
assertThat(KeyspaceIdentifier.isValid(""), is(false));
assertThat(KeyspaceIdentifier.isValid("foo:bar"), is(true));
assertThat(KeyspaceIdentifier.isValid("foo:bar:baz"), is(true));
assertThat(KeyspaceIdentifier.isValid("foo:bar:baz:phantom"), is(true));
}
@Test // DATAREDIS-744
public void shouldReturnKeyspace() {
assertThat(KeyspaceIdentifier.of("foo:bar").getKeyspace(), is("foo"));
assertThat(KeyspaceIdentifier.of("foo:bar:baz").getKeyspace(), is("foo"));
assertThat(KeyspaceIdentifier.of("foo:bar:baz:phantom").getKeyspace(), is("foo"));
}
@Test // DATAREDIS-744
public void shouldReturnId() {
assertThat(KeyspaceIdentifier.of("foo:bar").getId(), is("bar"));
assertThat(KeyspaceIdentifier.of("foo:bar:baz").getId(), is("bar:baz"));
assertThat(KeyspaceIdentifier.of("foo:bar:baz:phantom").getId(), is("bar:baz"));
}
@Test // DATAREDIS-744
public void shouldReturnPhantomKey() {
assertThat(KeyspaceIdentifier.of("foo:bar").isPhantomKey(), is(false));
assertThat(KeyspaceIdentifier.of("foo:bar:baz").isPhantomKey(), is(false));
assertThat(KeyspaceIdentifier.of("foo:bar:baz:phantom").isPhantomKey(), is(true));
}
}