Skip to content

Commit

Permalink
Improvements to collections lazy loading semantics, also added some m…
Browse files Browse the repository at this point in the history
…ore tests.
  • Loading branch information
gsharma authored and Jonathan Leibiusky committed Oct 12, 2010
1 parent 9a1cbab commit 8afd09f
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 57 deletions.
134 changes: 92 additions & 42 deletions src/main/java/redis/clients/johm/collections/RedisList.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,60 +10,60 @@
import redis.clients.johm.Model;
import redis.clients.johm.Nest;

/**
* RedisList is a JOhm-internal List implementation to serve as a proxy for the
* Redis persisted list and provide lazy-loading semantics to minimize datastore
* network traffic. It does a best-effort job of minimizing list entity
* staleness but does so without any locking and is not thread-safe. Only add
* and remove operations trigger a remote-sync of local internal storage.
*/
public class RedisList<T extends Model> implements java.util.List<T> {
private Nest nest;
private Class<? extends Model> clazz;
private List<T> elements;
private final List<T> elements;

public RedisList(Class<? extends Model> clazz, Nest nest) {
this.clazz = clazz;
this.nest = nest;
}

@SuppressWarnings("unchecked")
private List<T> getElements() {
if (elements == null) {
elements = new ArrayList<T>();
List<String> ids = nest.lrange(0, -1);
for (String id : ids) {
elements.add((T) JOhm.get(clazz, Integer.valueOf(id)));
}
}
return elements;
elements = new ArrayList<T>();
}

public boolean add(T e) {
return nest.rpush(e.getId().toString()) > 0;
return internalAdd(e, true);
}

public void add(int index, T element) {
nest.lset(index, element.getId().toString());
internalIndexedAdd(index, element, true);
}

public boolean addAll(Collection<? extends T> c) {
boolean success = true;
for (T element : c) {
nest.rpush(element.getId().toString());
success &= internalAdd(element, false);
}
return true;
refreshStorage();
return success;
}

public boolean addAll(int index, Collection<? extends T> c) {
for (T element : c) {
nest.lset(index++, element.getId().toString());
internalIndexedAdd(index++, element, false);
}
refreshStorage();
return true;
}

public void clear() {
nest.del();
refreshStorage();
}

public boolean contains(Object o) {
return getElements().contains(o);
return scrollElements().contains(o);
}

public boolean containsAll(Collection<?> c) {
return getElements().containsAll(c);
return scrollElements().containsAll(c);
}

@SuppressWarnings("unchecked")
Expand All @@ -73,84 +73,134 @@ public T get(int index) {
}

public int indexOf(Object o) {
return getElements().indexOf(o);
return scrollElements().indexOf(o);
}

public boolean isEmpty() {
return nest.llen() == 0;
return this.size() == 0;
}

public Iterator<T> iterator() {
return getElements().iterator();
return scrollElements().iterator();
}

public int lastIndexOf(Object o) {
return getElements().lastIndexOf(o);
return scrollElements().lastIndexOf(o);
}

public ListIterator<T> listIterator() {
return getElements().listIterator();
return scrollElements().listIterator();
}

public ListIterator<T> listIterator(int index) {
return getElements().listIterator(index);
return scrollElements().listIterator(index);
}

public boolean remove(Object o) {
Model element = (Model) o;
Integer lrem = nest.lrem(1, element.getId().toString());
return lrem > 0;
return internalRemove(o, true);
}

public T remove(int index) {
T element = this.get(index);
nest.lrem(1, element.getId().toString());
return element;
return internalIndexedRemove(index, true);
}

@SuppressWarnings("unchecked")
public boolean removeAll(Collection<?> c) {
boolean success = true;
Iterator<? extends Model> iterator = (Iterator<? extends Model>) c
.iterator();
while (iterator.hasNext()) {
T element = (T) iterator.next();
this.remove(element);
success &= internalRemove(element, false);
}
return true;
refreshStorage();
return success;
}

@SuppressWarnings("unchecked")
public boolean retainAll(Collection<?> c) {
this.clear();
Iterator<? extends Model> iterator = (Iterator<? extends Model>) c
.iterator();
boolean success = true;
while (iterator.hasNext()) {
T element = (T) iterator.next();
this.add(element);
success &= internalAdd(element, false);
}
return true;
refreshStorage();
return success;
}

public T set(int index, T element) {
T previousElement = get(index);
nest.lset(index, element.getId().toString());
T previousElement = this.get(index);
internalIndexedAdd(index, element, true);
return previousElement;
}

public int size() {
return nest.llen();
int repoSize = nest.llen();
if (repoSize != elements.size()) {
refreshStorage();
}
return repoSize;
}

public java.util.List<T> subList(int fromIndex, int toIndex) {
return getElements().subList(fromIndex, toIndex);
return scrollElements().subList(fromIndex, toIndex);
}

public Object[] toArray() {
return getElements().toArray();
return scrollElements().toArray();
}

@SuppressWarnings("hiding")
public <T> T[] toArray(T[] a) {
return getElements().toArray(a);
return scrollElements().toArray(a);
}

private boolean internalAdd(T e, boolean refreshStorage) {
boolean success = nest.rpush(e.getId().toString()) > 0;
if (refreshStorage) {
refreshStorage();
}
return success;
}

private void internalIndexedAdd(int index, T element, boolean refreshStorage) {
nest.lset(index, element.getId().toString());
if (refreshStorage) {
refreshStorage();
}
}

private boolean internalRemove(Object o, boolean refreshStorage) {
Model element = (Model) o;
Integer lrem = nest.lrem(1, element.getId().toString());
if (refreshStorage) {
refreshStorage();
}
return lrem > 0;
}

private T internalIndexedRemove(int index, boolean refreshStorage) {
T element = this.get(index);
internalRemove(element, refreshStorage);
return element;
}

private synchronized void refreshStorage() {
elements.clear();
scrollElements();
}

@SuppressWarnings("unchecked")
private synchronized List<T> scrollElements() {
if (elements.isEmpty()) {
List<String> ids = nest.lrange(0, -1);
for (String id : ids) {
elements.add((T) JOhm.get(clazz, Integer.valueOf(id)));
}
}
return elements;
}
}
64 changes: 49 additions & 15 deletions src/main/java/redis/clients/johm/collections/RedisSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
import redis.clients.johm.Model;
import redis.clients.johm.Nest;

/**
* RedisSet is a JOhm-internal Set implementation to serve as a proxy for the
* Redis persisted set and provide lazy-loading semantics to minimize datastore
* network traffic. It does a best-effort job of minimizing set entity staleness
* but does so without any locking and is not thread-safe. It also maintains
* whatever order in which Redis returns its set elements. Only add and remove
* trigger a remote-sync of local internal storage.
*/
public class RedisSet<T extends Model> implements Set<T> {
private final Nest nest;
private final Class<? extends Model> clazz;
Expand All @@ -22,7 +30,11 @@ public RedisSet(final Class<? extends Model> clazz, final Nest nest) {

@Override
public int size() {
return nest.smembers().size();
int repoSize = nest.smembers().size();
if (repoSize != elements.size()) {
refreshStorage();
}
return repoSize;
}

@Override
Expand Down Expand Up @@ -53,19 +65,12 @@ public <T> T[] toArray(T[] a) {

@Override
public boolean add(T element) {
return nest.sadd(element.getId().toString()) > 0;
return internalAdd(element, true);
}

@Override
public boolean remove(Object o) {
// Since we cannot guarantee all Model's will provide a reasonable
// equals() and hashCode() implementation, using remove() on the Set
// cannot guarantee container-storage purge.
if (!elements.isEmpty()) {
elements.clear();
}
Model element = Model.class.cast(o);
return nest.srem(element.getId().toString()) > 0;
return internalRemove(o, true);
}

@Override
Expand All @@ -77,8 +82,9 @@ public boolean containsAll(Collection<?> c) {
public boolean addAll(Collection<? extends T> collection) {
boolean success = true;
for (T element : collection) {
success &= this.add(element);
success &= internalAdd(element, false);
}
refreshStorage();
return success;
}

Expand All @@ -91,8 +97,9 @@ public boolean retainAll(Collection<?> c) {
boolean success = true;
while (iterator.hasNext()) {
T element = (T) iterator.next();
success &= this.add(element);
success &= internalAdd(element, false);
}
refreshStorage();
return success;
}

Expand All @@ -104,18 +111,45 @@ public boolean removeAll(Collection<?> c) {
boolean success = true;
while (iterator.hasNext()) {
T element = (T) iterator.next();
success &= this.remove(element);
success &= internalRemove(element, false);
}
refreshStorage();
return success;
}

@Override
public void clear() {
nest.del();
elements.clear();
}

private boolean internalAdd(T element, boolean refreshStorage) {
boolean success = nest.sadd(element.getId().toString()) > 0;
if (refreshStorage) { // don't trust success-value too much
refreshStorage();
}
return success;
}

private boolean internalRemove(Object o, boolean refreshStorage) {
Model element = Model.class.cast(o);
boolean success = nest.srem(element.getId().toString()) > 0;
if (refreshStorage) { // don't trust success-value too much
// Since we cannot guarantee all Model's will provide a reasonable
// equals() and hashCode() implementation, using remove() on the Set
// cannot guarantee container-storage purge.
refreshStorage();
}
return success;
}

private synchronized void refreshStorage() {
elements.clear();
scrollElements();
}

@SuppressWarnings("unchecked")
private Set<T> scrollElements() {
private synchronized Set<T> scrollElements() {
if (elements.isEmpty()) {
Set<String> ids = nest.smembers();
for (String id : ids) {
Expand All @@ -124,4 +158,4 @@ private Set<T> scrollElements() {
}
return elements;
}
}
}
Loading

0 comments on commit 8afd09f

Please sign in to comment.