Skip to content

Commit

Permalink
Adding tx.DataStructureFactory which builds proxy objects
Browse files Browse the repository at this point in the history
that use a TransactionalNodeStore to commit all writes
after a data structure operation.

This allows the user to wrap operations in locks they
are able to coordinate between all clients.
  • Loading branch information
basking2 committed Oct 21, 2012
1 parent 0039a9a commit bb9f5cd
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 41 deletions.
3 changes: 0 additions & 3 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,4 @@
o CouchDB
o Other?
- Implement other data structures / algorithms?
- Transactions
o SubTransactions do not have visibility into parent transaction
operations. They should.
- Implement Sorted Map Interface to BTree.
13 changes: 6 additions & 7 deletions src/main/java/org/sdsai/dsds/PagedList.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ public class PagedList<STOREKEY, V>
*/
private int pageSize;

public PagedList(final NodeStore<STOREKEY, STOREKEY, V> nodeStore,
final STOREKEY headKey,
final int pageSize )
public PagedList(final STOREKEY headKey,
final NodeStore<STOREKEY, STOREKEY, V> nodeStore,
final int pageSize)
{
this.pageSize = pageSize;
this.nodeStore = nodeStore;
Expand All @@ -66,13 +66,12 @@ public PagedList(final NodeStore<STOREKEY, STOREKEY, V> nodeStore,
getHead();
}

public PagedList(final NodeStore<STOREKEY, STOREKEY, V> nodeStore,
final STOREKEY headKey)
public PagedList(final STOREKEY headKey,
final NodeStore<STOREKEY, STOREKEY, V> nodeStore)
{
this(nodeStore, headKey, 100);
this(headKey, nodeStore, 100);
}


private Node<STOREKEY, STOREKEY> getHead()
{
Node<STOREKEY, STOREKEY> root;
Expand Down
89 changes: 69 additions & 20 deletions src/main/java/org/sdsai/dsds/node/tx/DataStructureFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;

import java.util.List;
import java.util.Map;

import org.sdsai.dsds.BTree;
import org.sdsai.dsds.PagedList;
import org.sdsai.dsds.node.NodeStore;
Expand All @@ -13,38 +16,42 @@
/**
* Build transactional datastructures using {@link Proxy}.
*/
public class DataStructureFactory<USERKEY, STOREKEY, VALUE>
public class DataStructureFactory
{
private NodeStore<USERKEY, STOREKEY, VALUE> nodeStore;

/**
* Build a new DataStructureFactory.
*
* @throws RuntimeException If a TransactionalNodeStore is passed as the nodeStore parameter.
*/
public DataStructureFactory(final NodeStore<USERKEY, STOREKEY, VALUE> nodeStore)
private DataStructureFactory()
{
}

private static <USERKEY, STOREKEY, VALUE> TransactionalNodeStore<USERKEY, STOREKEY, VALUE> txNodeStore(
final NodeStore<USERKEY, STOREKEY, VALUE> nodeStore)
{
if ( nodeStore instanceof TransactionalNodeStore )
{
throw new RuntimeException("DataStructureFactory is incompatible with TransactionalNodeStore.");
}

this.nodeStore = nodeStore;
return new TransactionalNodeStore<USERKEY, STOREKEY, VALUE>(nodeStore);
}

private InvocationHandler buildTxCommittingInvocationHandler()
private static <USERKEY, STOREKEY, VALUE> InvocationHandler buildTxCommittingInvocationHandler(
final NodeStore<USERKEY, STOREKEY, VALUE> nodeStore,
final Object dispatchObject)
{
final TransactionalNodeStore<USERKEY, STOREKEY, VALUE> transactionalNodeStore =
new TransactionalNodeStore<USERKEY, STOREKEY, VALUE>(nodeStore);

final TransactionalNodeStore<USERKEY, STOREKEY, VALUE> transactionalNodeStore = txNodeStore(nodeStore);
final InvocationHandler ivh = new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args)
public Object invoke(final Object proxy, final Method method, final Object[] args)
{
try
{
final Object returnObject = method.invoke(proxy, args);
final Object returnObject = method.invoke(dispatchObject, args);

transactionalNodeStore.commit();

Expand All @@ -64,27 +71,69 @@ public Object invoke(Object proxy, Method method, Object[] args)
return ivh;
}

public PagedList<STOREKEY, VALUE> pagedList()
public static <STOREKEY, VALUE> List<VALUE> pagedList(
final STOREKEY root,
final NodeStore<STOREKEY, STOREKEY, VALUE> nodeStore,
final int size)
{
final PagedList<STOREKEY, VALUE> pagedList = new PagedList<STOREKEY, VALUE>(root, nodeStore, size);

@SuppressWarnings("unchecked")
final PagedList<STOREKEY, VALUE> pagedList = (PagedList<STOREKEY, VALUE>)
final List<VALUE> list = (List<VALUE>)
Proxy.newProxyInstance(
PagedList.class.getClassLoader(),
new Class<?>[]{ PagedList.class },
buildTxCommittingInvocationHandler());
new Class<?>[]{ List.class },
buildTxCommittingInvocationHandler(nodeStore, pagedList));

return pagedList;
return list;
}

public BTree<USERKEY, STOREKEY, VALUE> bTree()
public static <STOREKEY, VALUE> List<VALUE> pagedList(
final STOREKEY root,
final NodeStore<STOREKEY, STOREKEY, VALUE> nodeStore)
{
final PagedList<STOREKEY, VALUE> pagedList = new PagedList<STOREKEY, VALUE>(root, nodeStore);

@SuppressWarnings("unchecked")
final List<VALUE> list = (List<VALUE>)
Proxy.newProxyInstance(
PagedList.class.getClassLoader(),
new Class<?>[]{ List.class },
buildTxCommittingInvocationHandler(nodeStore, pagedList));

return list;
}

public static <USERKEY, STOREKEY, VALUE> Map<USERKEY, VALUE> bTree(
final USERKEY root,
final NodeStore<USERKEY, STOREKEY, VALUE> nodeStore,
final int size)
{
final BTree<USERKEY, STOREKEY, VALUE> bTree = new BTree<USERKEY, STOREKEY, VALUE>(root, nodeStore, size);

@SuppressWarnings("unchecked")
final Map<USERKEY, VALUE> map = (Map<USERKEY, VALUE>)
Proxy.newProxyInstance(
BTree.class.getClassLoader(),
new Class<?>[]{ Map.class },
buildTxCommittingInvocationHandler(nodeStore, bTree));

return map;
}

public static <USERKEY, STOREKEY, VALUE> Map<USERKEY, VALUE> bTree(
final USERKEY root,
final NodeStore<USERKEY, STOREKEY, VALUE> nodeStore)
{
final BTree<USERKEY, STOREKEY, VALUE> bTree = new BTree<USERKEY, STOREKEY, VALUE>(root, nodeStore);

@SuppressWarnings("unchecked")
final BTree<USERKEY, STOREKEY, VALUE> bTree = (BTree<USERKEY, STOREKEY, VALUE>)
final Map<USERKEY, VALUE> map = (Map<USERKEY, VALUE>)
Proxy.newProxyInstance(
BTree.class.getClassLoader(),
new Class<?>[]{ BTree.class },
buildTxCommittingInvocationHandler());
new Class<?>[]{ Map.class },
buildTxCommittingInvocationHandler(nodeStore, bTree));

return bTree;
return map;
}
}
46 changes: 42 additions & 4 deletions src/main/java/org/sdsai/dsds/node/tx/TransactionalNodeStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,32 @@ public class TransactionalNodeStore<USERKEY, STOREKEY, VALUE>

private Map<STOREKEY, Operation<STOREKEY, Node<USERKEY, STOREKEY>>> nodes;

private TransactionalNodeStore<USERKEY, STOREKEY, VALUE> parentTransaction;

public TransactionalNodeStore(final NodeStore<USERKEY, STOREKEY, VALUE> nodeStore)
{
this(nodeStore, null);
}

/**
* Create a new transaction that operates against the given {@link NodeStore}.
*/
public TransactionalNodeStore(final NodeStore<USERKEY, STOREKEY, VALUE> nodeStore)
public TransactionalNodeStore(final NodeStore<USERKEY, STOREKEY, VALUE> nodeStore, final TransactionalNodeStore<USERKEY, STOREKEY, VALUE> parentTransaction)
{
this.nodeStore = nodeStore;
this.operations = new LinkedList<Operation<?,?>>();
this.values = new HashMap<STOREKEY, Operation<STOREKEY, VALUE>>();
this.nodes = new HashMap<STOREKEY, Operation<STOREKEY, Node<USERKEY, STOREKEY>>>();
this.parentTransaction = parentTransaction;
}

/**
* Give the user a sub-transaction which is merged into this transaction when {@link SubTx#buildTx(TransactionalNodeStore)} returns.
*/
public void subTransaction(final SubTx<USERKEY, STOREKEY, VALUE> subTx)
{
final TransactionalNodeStore<USERKEY, STOREKEY, VALUE> tx = new TransactionalNodeStore<USERKEY, STOREKEY, VALUE>(nodeStore);
final TransactionalNodeStore<USERKEY, STOREKEY, VALUE> tx =
new TransactionalNodeStore<USERKEY, STOREKEY, VALUE>(nodeStore, this);

subTx.buildTx(tx);

Expand Down Expand Up @@ -100,13 +109,28 @@ public STOREKEY generateKey(final Node<USERKEY,STOREKEY> node, final VALUE value
return nodeStore.generateKey(node, value);
}

/**
* Attempt to find a read operation in this transaction or any parent transaction.
*/
private Operation<STOREKEY, VALUE> loadDataReadOperation(final STOREKEY key)
{
Operation<STOREKEY, VALUE> readOperation = values.get(key);

if (readOperation == null && parentTransaction != null)
{
readOperation = parentTransaction.loadDataReadOperation(key);
}

return readOperation;
}

/**
* {@inheritDoc}
*/
@Override
public VALUE loadData(final STOREKEY key)
{
final Operation<STOREKEY, VALUE> readOperation = values.get(key);
final Operation<STOREKEY, VALUE> readOperation = loadDataReadOperation(key);

if ( readOperation == null )
{
Expand All @@ -122,13 +146,27 @@ public VALUE loadData(final STOREKEY key)
}
}

/**
* Attempt to find a read operation in this transaction or any parent transaction.
*/
private Operation<STOREKEY, Node<USERKEY, STOREKEY>> loadNodeReadOperation(final STOREKEY key)
{
Operation<STOREKEY, Node<USERKEY, STOREKEY>> readOperation = nodes.get(key);

if (readOperation == null && parentTransaction != null)
{
readOperation = parentTransaction.loadNodeReadOperation(key);
}

return readOperation;
}
/**
* {@inheritDoc}
*/
@Override
public Node<USERKEY, STOREKEY> loadNode(final STOREKEY key)
{
final Operation<STOREKEY, Node<USERKEY, STOREKEY>> readOperation = nodes.get(key);
final Operation<STOREKEY, Node<USERKEY, STOREKEY>> readOperation = loadNodeReadOperation(key);

if ( readOperation == null )
{
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/org/sdsai/dsds/BTreeIteratorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private void bkwdIteration(final BTree<UUID, File, String> bt)
public void forwardsBackwardsTest()
{
final NodeStore<Integer, File, String> ns =
new DirectoryNodeStore("target/forwadsBackwardsTest");
new DirectoryNodeStore<Integer, String>("target/forwadsBackwardsTest");

final Integer btId = 0;

Expand Down
6 changes: 3 additions & 3 deletions src/test/java/org/sdsai/dsds/PagedListTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ private void print(final PagedList<File, String> p)
public void testAddRemove()
{
PagedList<File, String> pl =
new PagedList<File, String>(nodeStore, nodeStore.generateKey(null, null), 3);
new PagedList<File, String>(nodeStore.generateKey(null, null), nodeStore, 3);

try
{
Expand Down Expand Up @@ -108,7 +108,7 @@ public void testAddRemove()
public void testAddRandomRemove()
{
PagedList<File, String> pl =
new PagedList<File, String>(nodeStore, nodeStore.generateKey(null, null), 3);
new PagedList<File, String>(nodeStore.generateKey(null, null), nodeStore, 3);

try
{
Expand Down Expand Up @@ -143,7 +143,7 @@ public void testAddRandomRemove()
public void testAddRandom()
{
PagedList<File, String> pl =
new PagedList<File, String>(nodeStore, nodeStore.generateKey(null, null), 3);
new PagedList<File, String>(nodeStore.generateKey(null, null), nodeStore, 3);

try
{
Expand Down
53 changes: 51 additions & 2 deletions src/test/java/org/sdsai/dsds/node/tx/DataStructureFactoryTest.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,62 @@
package org.sdsai.dsds.node.tx;

import java.util.UUID;
import java.util.Map;
import java.util.List;

import org.sdsai.dsds.BaseTest;
import org.sdsai.dsds.BTree;
import org.sdsai.dsds.node.NodeStore;
import org.sdsai.dsds.fs.DirectoryNodeStore;
import java.io.File;
import java.io.Serializable;

import org.junit.Test;
import org.junit.Before;
import org.junit.Ignore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DataStructureFactoryTest extends BaseTest<UUID>
import static java.util.UUID.randomUUID;
import static org.junit.Assert.assertEquals;

public class DataStructureFactoryTest
{
private final Logger logger = LoggerFactory.getLogger(BTreeTest.class);
private final Logger logger = LoggerFactory.getLogger(DataStructureFactoryTest.class);

private static <USERKEY extends Serializable> NodeStore<USERKEY, File, String> buildNodeStore(final Class<USERKEY> clazz)
{
final NodeStore<USERKEY, File, String> nodeStore =
new DirectoryNodeStore<USERKEY, String>("target/DirectoryNodeStore/"+DataStructureFactoryTest.class.getSimpleName());

return nodeStore;
}

@Test
public void testTxBTree()
{
final UUID btKey = randomUUID();
final Map<UUID, String> map = DataStructureFactory.bTree(btKey, buildNodeStore(UUID.class));
}

@Test
public void testTxPagedList()
{
final NodeStore<File, File, String> nodeStore = buildNodeStore(File.class);

final List<String> list = DataStructureFactory.pagedList(nodeStore.generateKey(null, null), nodeStore, 3);

for(int i = 0; i < 100; i++)
{
list.add("Hi "+i);
}

for(int i = 0; i < 100; i++)
{
final String s = "Hi "+i;
assertEquals(s, list.get(i));
}

list.clear();
}
}
2 changes: 1 addition & 1 deletion src/test/java/org/sdsai/dsds/riak/RiakNodeStoreTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void setup() throws UnknownHostException
{
final IRiakClient riakClient = RiakFactory.httpClient();

ns = new RiakNodeStore(riakClient, nodeBucket, dataBucket, String.class);
ns = new RiakNodeStore<String, String>(riakClient, nodeBucket, dataBucket, String.class);

bt = new BTree<String, String, String>("btRoot", ns, 1);

Expand Down

0 comments on commit bb9f5cd

Please sign in to comment.