You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by to...@apache.org on 2013/11/27 23:24:42 UTC

[1/3] Initial import of 2.0 core persistence code.

Updated Branches:
  refs/heads/two-dot-o [created] ac634a1d0


http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftest.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftest.java
new file mode 100644
index 0000000..7a71815
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftest.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest;
+
+import org.apache.usergrid.perftest.logging.Log;
+import org.slf4j.Logger;
+
+/**
+ * A performance test that does nothing.
+ */
+public class NoopPerftest implements Perftest {
+    @Log Logger log;
+
+
+    @Override
+    public int getCallCount() {
+        return 1000;
+    }
+
+
+    @Override
+    public int getThreadCount() {
+        return 10;
+    }
+
+
+    @Override
+    public int getDelayBetweenCalls() {
+        return 0;
+    }
+
+
+    @Override
+    public void call() {
+        log.info( "This performance test tests nothing" );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftestModule.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftestModule.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftestModule.java
new file mode 100644
index 0000000..1dbe087
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/NoopPerftestModule.java
@@ -0,0 +1,34 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest;
+
+
+import com.google.inject.AbstractModule;
+
+
+/**
+ * A performance test module that does nothing.
+ */
+public class NoopPerftestModule extends AbstractModule {
+    @Override
+    protected void configure() {
+        bind( Perftest.class ).to( NoopPerftest.class );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/Perftest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/Perftest.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/Perftest.java
new file mode 100644
index 0000000..14388e3
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/Perftest.java
@@ -0,0 +1,11 @@
+package org.apache.usergrid.perftest;
+
+/**
+ * A performance test that will be run.
+ */
+public interface Perftest {
+    int getCallCount();
+    int getThreadCount();
+    int getDelayBetweenCalls();
+    void call();
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestModule.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestModule.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestModule.java
new file mode 100644
index 0000000..9f8581b
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestModule.java
@@ -0,0 +1,47 @@
+/*
+ * Created by IntelliJ IDEA.
+ * User: akarasulu
+ * Date: 11/22/13
+ * Time: 11:44 PM
+ */
+package org.apache.usergrid.perftest;
+
+import org.apache.usergrid.perftest.logging.Slf4jTypeListener;
+import org.apache.usergrid.perftest.rest.*;
+import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
+import com.google.inject.matcher.Matchers;
+import com.google.inject.servlet.ServletModule;
+import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
+import org.apache.usergrid.perfteststats.CallStats;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class PerftestModule extends ServletModule {
+    public static final String PACKAGES_KEY = "com.sun.jersey.config.property.packages";
+
+
+    protected void configureServlets() {
+        bindListener( Matchers.any(), new Slf4jTypeListener() );
+
+        // Hook Jersey into Guice Servlet
+        bind( GuiceContainer.class );
+
+        // Hook Jackson into Jersey as the POJO <-> JSON mapper
+        bind( JacksonJsonProvider.class ).asEagerSingleton();
+
+        bind( CallStats.class );
+        bind( PerftestRunner.class );
+        bind( TestModuleLoader.class );
+        bind( PerftestResetResource.class ).asEagerSingleton();
+        bind( PerftestStopResource.class ).asEagerSingleton();
+        bind( PerftestStartResource.class ).asEagerSingleton();
+        bind( PerftestStatsResource.class ).asEagerSingleton();
+        bind( PerftestStatusResource.class ).asEagerSingleton();
+
+        Map<String, String> params = new HashMap<String, String>();
+        params.put( PACKAGES_KEY, getClass().getPackage().toString() );
+        serve("/*").with( GuiceContainer.class, params );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestRunner.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestRunner.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestRunner.java
new file mode 100644
index 0000000..a8bd831
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestRunner.java
@@ -0,0 +1,197 @@
+package org.apache.usergrid.perftest;
+
+
+import com.netflix.blitz4j.LoggingConfiguration;
+import org.apache.usergrid.perftest.logging.Log;
+import org.apache.usergrid.perftest.rest.CallStatsSnapshot;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Singleton;
+import com.netflix.config.DynamicLongProperty;
+import com.netflix.config.DynamicPropertyFactory;
+import org.apache.usergrid.perfteststats.CallStats;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Invokes a Perftest based on a CallSpec.
+ */
+@Singleton
+public class PerftestRunner {
+    @Log
+    Logger log;
+
+
+    private final TestModuleLoader loader;
+    private final Injector injector;
+    private final Object lock = new Object();
+    private DynamicLongProperty sleepToStop = DynamicPropertyFactory.getInstance().getLongProperty( "sleep.to.stop", 100 );
+    private CallStats stats;
+    private List<Thread> threads = new ArrayList<Thread>();
+    private boolean stopSignal = false;
+    private boolean running = false;
+    private boolean needsReset = false;
+    private long startTime;
+    private long stopTime;
+
+
+    @Inject
+    public PerftestRunner( Injector injector, TestModuleLoader loader )
+    {
+        this.loader = loader;
+        this.injector = injector;
+        setup();
+    }
+
+
+    public void setup() {
+        synchronized ( lock ) {
+            this.threads.clear();
+            this.stopSignal = false;
+            this.running = false;
+            this.startTime = 0;
+            this.stopTime = 0;
+
+            this.stats = injector.getInstance( CallStats.class );
+            final Perftest test = loader.getChildInjector().getInstance(Perftest.class);
+
+            final long delay = test.getDelayBetweenCalls();
+            threads = new ArrayList<Thread>( test.getThreadCount() );
+            for ( int ii = 0; ii < test.getThreadCount(); ii++ ) {
+                threads.add( new Thread( new Runnable() {
+                    @Override
+                    public void run() {
+                        while( ( ! stopSignal ) && ( stats.getCallCount() < test.getCallCount() ) ) {
+                            long startTime = System.nanoTime();
+                            test.call();
+                            long endTime = System.nanoTime();
+                            stats.callOccurred( test, startTime, endTime, TimeUnit.NANOSECONDS );
+
+                            if ( delay > 0 )
+                            {
+                                try {
+                                    Thread.sleep( delay );
+                                } catch ( InterruptedException e ) {
+                                    log.error( "Thread was interrupted.", e );
+                                }
+                            }
+
+                            synchronized ( lock ) {
+                                lock.notifyAll();
+                            }
+                        }
+                    }
+                }) );
+            }
+
+            this.needsReset = false;
+        }
+    }
+
+
+    public CallStatsSnapshot getCallStatsSnapshot() {
+        return stats.getStatsSnapshot( isRunning(), getStartTime(), getStopTime() );
+    }
+
+
+    public boolean isRunning() {
+        synchronized ( lock ) {
+            return running;
+        }
+    }
+
+
+    public boolean needsReset() {
+        synchronized ( lock )
+        {
+            return needsReset;
+        }
+    }
+
+
+    public long getStartTime() {
+        return startTime;
+    }
+
+
+    public long getStopTime() {
+        return stopTime;
+    }
+
+
+    public void start() {
+        synchronized ( lock ) {
+            stopSignal = false;
+            startTime = System.nanoTime();
+            running = true;
+            for ( Thread t : threads ) {
+                t.start();
+            }
+        }
+
+        // launch a coordinator thread to detect when all others are done
+        new Thread( new Runnable() {
+            @Override
+            public void run() {
+                while ( threadsRunning() )
+                {
+                    synchronized ( lock )
+                    {
+                        try {
+                            lock.wait( sleepToStop.get() );
+                            log.info( "woke up running = {}", PerftestRunner.this.running );
+                            lock.notifyAll();
+                        } catch (InterruptedException e) {
+                            log.error( "Thread interrupted while sleeping", e );
+                        }
+                    }
+                }
+
+                log.info( "COORDINATOR THREAD: all threads have died." );
+                PerftestRunner.this.running = false;
+                PerftestRunner.this.needsReset = true;
+                stopTime = System.nanoTime();
+            }
+        } ).start();
+    }
+
+
+    private boolean threadsRunning()
+    {
+        boolean anyAlive = false;
+
+        try {
+            Thread.sleep( sleepToStop.get() );
+        }
+        catch ( InterruptedException e ) {
+            log.error( "Thread was interrupted.", e );
+        }
+
+        for ( Thread t : threads )
+        {
+            anyAlive |= t.isAlive();
+        }
+
+        return anyAlive;
+    }
+
+
+    public void stop() {
+        synchronized ( lock ) {
+            stopSignal = true;
+            boolean anyAlive = false;
+
+            do {
+                anyAlive |= threadsRunning();
+            } while( anyAlive );
+
+            running = false;
+            stopTime = System.nanoTime();
+            needsReset = true;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestServletConfig.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestServletConfig.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestServletConfig.java
new file mode 100644
index 0000000..7f664a9
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/PerftestServletConfig.java
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.servlet.GuiceServletContextListener;
+import com.netflix.blitz4j.LoggingConfiguration;
+
+import javax.servlet.ServletContextEvent;
+
+/**
+ * ...
+ */
+public class PerftestServletConfig extends GuiceServletContextListener {
+    @Override
+    protected Injector getInjector() {
+        return Guice.createInjector( new PerftestModule() );
+    }
+
+
+    @Override
+    public void contextInitialized( ServletContextEvent servletContextEvent ) {
+        LoggingConfiguration.getInstance().configure();
+        super.contextInitialized( servletContextEvent );
+    }
+
+    @Override
+    public void contextDestroyed( ServletContextEvent servletContextEvent ) {
+        LoggingConfiguration.getInstance().stop();
+        super.contextDestroyed( servletContextEvent );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/TestModuleLoader.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/TestModuleLoader.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/TestModuleLoader.java
new file mode 100644
index 0000000..45c7ebd
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/TestModuleLoader.java
@@ -0,0 +1,95 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest;
+
+
+import com.google.inject.*;
+import com.netflix.config.DynamicPropertyFactory;
+import com.netflix.config.DynamicStringProperty;
+
+
+/**
+ * Dynamically loads the Guice Module responsible for creating the Perftest.
+ */
+@Singleton
+public class TestModuleLoader implements Runnable {
+    public static final String MOCK_TEST_MODULE = "org.apache.usergrid.perftest.NoopPerftestModule";
+
+    private final Injector injector;
+    private Injector childInjector;
+    private DynamicStringProperty testModuleFqcn;
+    private Module testModule;
+
+
+    @Inject
+    public TestModuleLoader( Injector injector )
+    {
+        this.injector = injector;
+        testModuleFqcn = DynamicPropertyFactory.getInstance().getStringProperty( "test.module.fqcn", MOCK_TEST_MODULE );
+
+        if ( testModuleFqcn.get().equals( MOCK_TEST_MODULE ) ) {
+            testModule = new NoopPerftestModule();
+        }
+        else {
+            testModule = loadTestModule();
+        }
+
+        childInjector = injector.createChildInjector( testModule );
+        testModuleFqcn.addCallback( this );
+    }
+
+
+    public Module loadTestModule() {
+        // This is a crappy mechanism now - we need to use OSGi for this
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+        try {
+            Class clazz = cl.loadClass( testModuleFqcn.get() );
+            return ( Module ) clazz.newInstance();
+        } catch ( ClassNotFoundException e ) {
+            e.printStackTrace();
+        } catch ( InstantiationException e ) {
+            e.printStackTrace();
+        } catch ( IllegalAccessException e ) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+
+    public Module getTestModule()
+    {
+        return testModule;
+    }
+
+
+    public Injector getChildInjector()
+    {
+        return childInjector;
+    }
+
+
+    @Override
+    public void run() {
+        testModule = loadTestModule();
+        childInjector = injector.createChildInjector( testModule );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Log.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Log.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Log.java
new file mode 100644
index 0000000..32ddfee
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Log.java
@@ -0,0 +1,19 @@
+package org.apache.usergrid.perftest.logging;
+
+import javax.inject.Scope;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Annotation to use to inject a SLF4J Logger backed by Log4j.
+ */
+@Scope
+@Documented
+@Retention( RUNTIME )
+@Target( FIELD )
+public @interface Log {
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jMembersInjector.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jMembersInjector.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jMembersInjector.java
new file mode 100644
index 0000000..7ccbe7f
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jMembersInjector.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest.logging;
+
+
+import com.google.inject.MembersInjector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Field;
+
+public class Slf4jMembersInjector<T> implements MembersInjector<T> {
+    private final Field field;
+    private final Logger logger;
+
+    public Slf4jMembersInjector(Field field) {
+        this.field = field;
+        this.logger = LoggerFactory.getLogger(field.getDeclaringClass());
+        field.setAccessible(true);
+    }
+
+    public void injectMembers(T t) {
+        try {
+            field.set(t, logger);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jTypeListener.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jTypeListener.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jTypeListener.java
new file mode 100644
index 0000000..7c43bd9
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/logging/Slf4jTypeListener.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest.logging;
+
+import com.google.inject.TypeLiteral;
+import com.google.inject.spi.TypeEncounter;
+import com.google.inject.spi.TypeListener;
+import org.slf4j.Logger;
+
+import java.lang.reflect.Field;
+
+/**
+ * ...
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class Slf4jTypeListener implements TypeListener {
+    public <T> void hear(TypeLiteral<T> typeLiteral, TypeEncounter<T> typeEncounter) {
+        for (Field field : typeLiteral.getRawType().getDeclaredFields()) {
+            if (field.getType() == Logger.class
+                    && field.isAnnotationPresent(Log.class)) {
+                typeEncounter.register(new Slf4jMembersInjector<T>(field));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/CallStatsSnapshot.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/CallStatsSnapshot.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/CallStatsSnapshot.java
new file mode 100644
index 0000000..f1fdd78
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/CallStatsSnapshot.java
@@ -0,0 +1,91 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest.rest;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * ...
+ */
+public class CallStatsSnapshot {
+    private final int callCount;
+    private final long maxTime;
+    private final long minTime;
+    private final long meanTime;
+    private final boolean running;
+    private final long startTime;
+    private final long stopTime;
+
+
+    public CallStatsSnapshot( int callCount, long maxTime, long minTime, long meanTime,
+                              boolean running, long startTime, long stopTime ) {
+        this.callCount = callCount;
+        this.maxTime = maxTime;
+        this.minTime = minTime;
+        this.meanTime = meanTime;
+        this.running = running;
+        this.startTime = startTime;
+        this.stopTime = stopTime;
+    }
+
+
+    @JsonProperty
+    public int getCallCount() {
+        return callCount;
+    }
+
+
+    @JsonProperty
+    public long getMaxTime() {
+        return maxTime;
+    }
+
+
+    @JsonProperty
+    public long getMinTime() {
+        return minTime;
+    }
+
+
+    @JsonProperty
+    public long getMeanTime() {
+        return meanTime;
+    }
+
+
+    @JsonProperty
+    public boolean isRunning() {
+        return running;
+    }
+
+
+    @JsonProperty
+    public long getStartTime()
+    {
+        return startTime;
+    }
+
+
+    @JsonProperty
+    public long getStopTime()
+    {
+        return stopTime;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestResetResource.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestResetResource.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestResetResource.java
new file mode 100644
index 0000000..402945b
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestResetResource.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest.rest;
+
+import org.apache.usergrid.perftest.PerftestRunner;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * ...
+ */
+@Singleton
+@Produces( MediaType.APPLICATION_JSON )
+@Path( "/perftest/reset" )
+public class PerftestResetResource {
+    private final PerftestRunner runner;
+
+
+    @Inject
+    public PerftestResetResource( PerftestRunner runner )
+    {
+        this.runner = runner;
+    }
+
+
+    @POST
+    public String reset()
+    {
+        if ( runner.isRunning() )
+        {
+            return "{ \"result\":\"still running stop before resetting\" }";
+        }
+
+        if ( runner.needsReset() )
+        {
+            runner.setup();
+            return "{ \"result\":\"reset complete\" }";
+        }
+
+        return "{ \"result\":\"reset not required\" }";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStartResource.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStartResource.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStartResource.java
new file mode 100644
index 0000000..0fb5705
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStartResource.java
@@ -0,0 +1,64 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest.rest;
+
+import org.apache.usergrid.perftest.PerftestRunner;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * ...
+ */
+@Singleton
+@Produces( MediaType.APPLICATION_JSON )
+@Path( "/perftest/start" )
+public class PerftestStartResource {
+    private final PerftestRunner runner;
+
+
+    @Inject
+    public PerftestStartResource( PerftestRunner runner )
+    {
+        this.runner = runner;
+    }
+
+
+    @POST
+    public String start()
+    {
+        if ( runner.isRunning() )
+        {
+            return "{ \"result\":\"already running\" }";
+        }
+
+        if ( runner.needsReset() )
+        {
+            return "{ \"result\":\"reset needed - but save the last run data first!\" }";
+        }
+
+        runner.start();
+        return "{ \"result\":\"successfully started\" }";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatsResource.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatsResource.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatsResource.java
new file mode 100644
index 0000000..93c2f20
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatsResource.java
@@ -0,0 +1,53 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest.rest;
+
+import org.apache.usergrid.perftest.PerftestRunner;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * ...
+ */
+@Singleton
+@Produces( MediaType.APPLICATION_JSON )
+@Path( "/perftest/perfteststats" )
+public class PerftestStatsResource {
+    private final PerftestRunner runner;
+
+
+    @Inject
+    public PerftestStatsResource( PerftestRunner runner )
+    {
+        this.runner = runner;
+    }
+
+
+    @GET
+    public CallStatsSnapshot getCallStatsSnapshot()
+    {
+        return runner.getCallStatsSnapshot();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatusResource.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatusResource.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatusResource.java
new file mode 100644
index 0000000..b146661
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStatusResource.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest.rest;
+
+import org.apache.usergrid.perftest.PerftestRunner;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * ...
+ */
+@Singleton
+@Produces( MediaType.APPLICATION_JSON )
+@Path( "/perftest/status" )
+public class PerftestStatusResource {
+    private final PerftestRunner runner;
+
+
+    @Inject
+    public PerftestStatusResource( PerftestRunner runner ) {
+        this.runner = runner;
+    }
+
+
+    @GET
+    public String status()
+    {
+        if ( runner.isRunning() )
+        {
+            return "{ \"status\":\"running\" }";
+        }
+
+        if ( runner.needsReset() )
+        {
+            return "{ \"status\":\"needs reset\" }";
+        }
+
+        return "{ \"status\":\"not running\" }";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStopResource.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStopResource.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStopResource.java
new file mode 100644
index 0000000..36d3590
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perftest/rest/PerftestStopResource.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perftest.rest;
+
+import org.apache.usergrid.perftest.PerftestRunner;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * ...
+ */
+@Singleton
+@Produces( MediaType.APPLICATION_JSON )
+@Path( "/perftest/stop" )
+public class PerftestStopResource {
+    private final PerftestRunner runner;
+
+
+    @Inject
+    public PerftestStopResource( PerftestRunner runner )
+    {
+        this.runner = runner;
+    }
+
+
+    @POST
+    public String stop()
+    {
+        if ( runner.isRunning() )
+        {
+            runner.stop();
+            return "{ \"result\":\"stopped\" }";
+        }
+
+        return "{ \"result\":\"already stopped\" }";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perfteststats/CallStats.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perfteststats/CallStats.java b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perfteststats/CallStats.java
new file mode 100644
index 0000000..e0a09a0
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/java/org/apache/usergrid/perfteststats/CallStats.java
@@ -0,0 +1,90 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.perfteststats;
+
+
+import org.apache.usergrid.perftest.Perftest;
+import org.apache.usergrid.perftest.logging.Log;
+import org.apache.usergrid.perftest.rest.CallStatsSnapshot;
+import org.slf4j.Logger;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+/**
+ * Atomically stores and updates call statistics on tests.
+ */
+public class CallStats {
+    @Log Logger log;
+
+    private final AtomicInteger callCount = new AtomicInteger();
+    private final Object lock = new Object();
+    private final TimeUnit units = TimeUnit.NANOSECONDS;
+
+    private long maxTime = Long.MIN_VALUE;
+    private long minTime = Long.MAX_VALUE;
+    private long meanTime = 0;
+    private long totalTime = 0;
+
+
+    public int getCallCount() {
+        synchronized ( lock ) {
+            return callCount.get();
+        }
+    }
+
+
+    public CallStatsSnapshot getStatsSnapshot( boolean isRunning, long startTime, long stopTime ) {
+        synchronized ( lock )
+        {
+            return new CallStatsSnapshot( callCount.get(), maxTime, minTime, meanTime, isRunning, startTime, stopTime );
+        }
+    }
+
+
+    public int callOccurred( Perftest test, long startTime, long endTime, TimeUnit units )
+    {
+        synchronized ( lock )
+        {
+            if ( callCount.get() >  test.getCallCount() - 1 )
+            {
+                return callCount.get();
+            }
+
+            if ( this.units.equals( units ) ) {
+                long time = endTime - startTime;
+
+                totalTime += time;
+                maxTime = Math.max( maxTime, time );
+                minTime = Math.min( minTime, time );
+                int numCalls = callCount.incrementAndGet();
+                StringBuilder sb = new StringBuilder();
+                sb.append( numCalls ).append( " " ).append( startTime ).append( " " ).append( endTime );
+                log.debug(sb.toString());
+                meanTime = totalTime / numCalls;
+                return numCalls;
+            }
+            else {
+                throw new RuntimeException( "Time unit corrections have not been implemented." );
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/resources/config.properties
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/resources/config.properties b/stack/corepersistence/perftest/src/main/resources/config.properties
new file mode 100644
index 0000000..f411991
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/resources/config.properties
@@ -0,0 +1,22 @@
+default.call.iterations=10000
+default.thread.count=10
+default.configuration.override=simpledb
+
+archaius.deployment.environment=test
+archaius.deployment.region=us-east-1c
+archaius.deployment.datacenter=Virginia
+archaius.deployment.applicationId=corepersistence-perftests
+
+archaius.dynamicPropertyFactory.registerConfigWithJMX=true
+
+#com.netflix.config.dynamo.tableName=archaiusProperties
+#com.netflix.config.dynamo.keyAttributeName=key
+#com.netflix.config.dynamo.valueAttributeName=value
+#com.netflix.config.dynamo.contextKeyAttributeName=contextKey
+#com.netflix.config.dynamo.contextValueAttributeName=contextValue
+
+# Application Settings
+
+test.module.fqcn=org.apache.usergrid.perftest.NoopPerftestModule
+leep.to.stop=100
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/resources/log4j.properties b/stack/corepersistence/perftest/src/main/resources/log4j.properties
new file mode 100644
index 0000000..a114a72
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/resources/log4j.properties
@@ -0,0 +1,20 @@
+log4j.rootLogger=OFF
+log4j.rootCategory=OFF
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %p %c{3}.%M(%L)<%t>- %m%n
+
+log4j.appender.results=org.apache.log4j.FileAppender
+log4j.appender.results.File=./results.log
+log4j.appender.results.layout=com.netflix.logging.log4jAdapter.NFPatternLayout
+log4j.appender.results.Append=true
+log4j.appender.results.layout.ConversionPattern=%m%n
+log4j.appender.results.Threshold=DEBUG
+
+log4j.logger.asyncAppenders=OFF,results,stdout
+batcher.com.netflix.logging.AsyncAppender.results.waitTimeinMillis=20000
+
+log4j.logger.org.apache.usergrid.perftest=INFO,stdout
+log4j.logger.org.apache.usergrid.perfteststats=DEBUG,results
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/src/main/webapp/WEB-INF/web.xml b/stack/corepersistence/perftest/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..310e6a3
--- /dev/null
+++ b/stack/corepersistence/perftest/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
+
+  <display-name>Guice Servlet</display-name>
+
+  <session-config>
+    <session-timeout>30</session-timeout>
+  </session-config>
+ 
+  <listener>
+    <listener-class>
+      org.apache.usergrid.perftest.PerftestServletConfig
+    </listener-class>
+  </listener>
+
+  <filter>
+    <filter-name>Guice Filter</filter-name>
+    <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
+  </filter>
+
+  <filter-mapping>
+    <filter-name>Guice Filter</filter-name>
+    <url-pattern>/*</url-pattern>
+  </filter-mapping>
+</web-app>

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/pom.xml b/stack/corepersistence/pom.xml
new file mode 100644
index 0000000..f09e9b0
--- /dev/null
+++ b/stack/corepersistence/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.usergrid</groupId>
+  <artifactId>persistence</artifactId>
+  <packaging>pom</packaging>
+  <version>1.0-SNAPSHOT</version>
+
+  <description>Prorotype for refactoring persistence of usergrid</description>
+
+  <properties >
+    <astynax.version>1.56.44</astynax.version>
+  </properties>
+
+  <modules>
+    <module>model</module>
+    <module>collection</module>
+    <module>index</module>
+    <module>testutils</module>
+    <!-- Removed perfest until the jackson/guice is sorted
+    <module>perftest</module> -->
+
+  </modules>
+
+
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/testutils/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/testutils/pom.xml b/stack/corepersistence/testutils/pom.xml
new file mode 100644
index 0000000..f863cf5
--- /dev/null
+++ b/stack/corepersistence/testutils/pom.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>persistence</artifactId>
+    <groupId>org.apache.usergrid</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>testutils</artifactId>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.11</version>
+    </dependency>
+
+    <!-- snappy for cassandra -->
+    <dependency>
+      <groupId>org.xerial.snappy</groupId>
+      <artifactId>snappy-java</artifactId>
+      <version>1.1.0.1</version>
+    </dependency>
+
+    <!-- cassandra runtime -->
+    <dependency>
+        <groupId>org.apache.cassandra</groupId>
+        <artifactId>cassandra-all</artifactId>
+        <version>1.2.11</version>
+    </dependency>
+
+
+    <dependency>
+      <groupId>com.netflix.astyanax</groupId>
+      <artifactId>astyanax-cassandra</artifactId>
+      <version>${astynax.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/AvailablePortFinder.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/AvailablePortFinder.java b/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/AvailablePortFinder.java
new file mode 100644
index 0000000..e2da676
--- /dev/null
+++ b/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/AvailablePortFinder.java
@@ -0,0 +1,187 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.usergrid.persistence.test;
+
+
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.ServerSocket;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.TreeSet;
+
+
+/**
+ * Finds currently available server ports.
+ *
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ * @see <a href="http://www.iana.org/assignments/port-numbers">IANA.org</a>
+ */
+public class AvailablePortFinder {
+    /** The minimum number of server port number. */
+    public static final int MIN_PORT_NUMBER = 1;
+
+    /** The maximum number of server port number. */
+    public static final int MAX_PORT_NUMBER = 49151;
+
+
+    /** Creates a new instance. */
+    private AvailablePortFinder() {
+        // Do nothing
+    }
+
+
+    /**
+     * Returns the {@link java.util.Set} of currently available port numbers ({@link Integer}).  This method is identical to
+     * <code>getAvailablePorts(MIN_PORT_NUMBER, MAX_PORT_NUMBER)</code>.
+     * <p/>
+     * WARNING: this can take a very long time.
+     */
+    public static Set<Integer> getAvailablePorts() {
+        return getAvailablePorts( MIN_PORT_NUMBER, MAX_PORT_NUMBER );
+    }
+
+
+    /**
+     * Gets an available port, selected by the system.
+     *
+     * @throws java.util.NoSuchElementException if there are no ports available
+     */
+    public static int getNextAvailable() {
+        ServerSocket serverSocket = null;
+
+        try {
+            // Here, we simply return an available port found by the system
+            serverSocket = new ServerSocket( 0 );
+            int port = serverSocket.getLocalPort();
+
+            // Don't forget to close the socket...
+            serverSocket.close();
+
+            return port;
+        }
+        catch ( IOException ioe ) {
+            throw new NoSuchElementException( ioe.getMessage() );
+        }
+    }
+
+
+    /**
+     * Gets the next available port starting at a port.
+     *
+     * @param fromPort the port to scan for availability
+     *
+     * @throws java.util.NoSuchElementException if there are no ports available
+     */
+    public static int getNextAvailable( int fromPort ) {
+        if ( fromPort < MIN_PORT_NUMBER || fromPort > MAX_PORT_NUMBER ) {
+            throw new IllegalArgumentException( "Invalid start port: " + fromPort );
+        }
+
+        for ( int i = fromPort; i <= MAX_PORT_NUMBER; i++ ) {
+            if ( available( i ) ) {
+                return i;
+            }
+        }
+
+        throw new NoSuchElementException( "Could not find an available port " + "above " + fromPort );
+    }
+
+
+    /**
+     * Checks to see if a specific port is available.
+     *
+     * @param port the port to check for availability
+     */
+    public static boolean available( int port ) {
+        if ( port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER ) {
+            throw new IllegalArgumentException( "Invalid start port: " + port );
+        }
+
+        ServerSocket ss = null;
+        DatagramSocket ds = null;
+
+        try {
+            ss = new ServerSocket( port );
+            ss.setReuseAddress( true );
+            ds = new DatagramSocket( port );
+            ds.setReuseAddress( true );
+            return true;
+        }
+        catch ( IOException e ) {
+            // Do nothing
+        }
+        finally {
+            if ( ds != null ) {
+                ds.close();
+            }
+
+            if ( ss != null ) {
+                try {
+                    ss.close();
+                }
+                catch ( IOException e ) {
+                    /* should not be thrown */
+                }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Returns the {@link java.util.Set} of currently avaliable port numbers ({@link Integer}) between the specified port range.
+     *
+     * @throws IllegalArgumentException if port range is not between {@link #MIN_PORT_NUMBER} and {@link
+     * #MAX_PORT_NUMBER} or <code>fromPort</code> if greater than <code>toPort</code>.
+     */
+    public static Set<Integer> getAvailablePorts( int fromPort, int toPort ) {
+        if ( fromPort < MIN_PORT_NUMBER || toPort > MAX_PORT_NUMBER || fromPort > toPort ) {
+            throw new IllegalArgumentException( "Invalid port range: " + fromPort + " ~ " + toPort );
+        }
+
+        Set<Integer> result = new TreeSet<Integer>();
+
+        for ( int i = fromPort; i <= toPort; i++ ) {
+            ServerSocket s = null;
+
+            try {
+                s = new ServerSocket( i );
+                result.add( new Integer( i ) );
+            }
+            catch ( IOException e ) {
+                // Do nothing
+            }
+            finally {
+                if ( s != null ) {
+                    try {
+                        s.close();
+                    }
+                    catch ( IOException e ) {
+                        /* should not be thrown */
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/CassandraRule.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/CassandraRule.java b/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/CassandraRule.java
new file mode 100644
index 0000000..989c00c
--- /dev/null
+++ b/stack/corepersistence/testutils/src/main/java/org/apache/usergrid/persistence/test/CassandraRule.java
@@ -0,0 +1,86 @@
+package org.apache.usergrid.persistence.test;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.rules.ExternalResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.io.util.FileUtils;
+
+import com.google.common.io.Files;
+import com.netflix.astyanax.test.EmbeddedCassandra;
+
+
+/**
+ * TODO, not sure if this is a hack,
+ *
+ * @author tnine
+ */
+public class CassandraRule extends ExternalResource {
+
+    private static final Logger logger = LoggerFactory.getLogger( CassandraRule.class );
+
+    public static final int THRIFT_PORT = AvailablePortFinder.getNextAvailable();
+
+    public static final int GOSSIP_PORT = AvailablePortFinder.getNextAvailable();
+
+
+    private static final Object mutex = new Object();
+
+    private static EmbeddedCassandra cass;
+
+    private static boolean started = false;
+
+    @Override
+    protected void before() throws Throwable {
+
+        if ( started ) {
+            return;
+        }
+
+        synchronized ( mutex ) {
+
+            //we're late to the party, bail
+            if ( started ) {
+                return;
+            }
+
+
+            File dataDir = Files.createTempDir();
+            dataDir.deleteOnExit();
+
+
+
+            //cleanup before we run, shouldn't be necessary, but had the directory exist during JVM kill
+            if(dataDir.exists()){
+                FileUtils.deleteRecursive(dataDir);
+            }
+
+            try {
+                logger.info( "Starting cassandra" );
+
+                cass = new EmbeddedCassandra( dataDir, "Usergrid", THRIFT_PORT, GOSSIP_PORT );
+                cass.start();
+
+                logger.info( "Cassandra started" );
+
+                started = true;
+            }
+            catch ( IOException e ) {
+                throw new RuntimeException( "Unable to start cassandra", e );
+            }
+        }
+    }
+
+
+    @Override
+    protected void after() {
+
+        //TODO TN.  this should only really happen when we shut down
+//        cass.stop();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/testutils/src/main/resources/log4j-server.properties
----------------------------------------------------------------------
diff --git a/stack/corepersistence/testutils/src/main/resources/log4j-server.properties b/stack/corepersistence/testutils/src/main/resources/log4j-server.properties
new file mode 100644
index 0000000..cf22bca
--- /dev/null
+++ b/stack/corepersistence/testutils/src/main/resources/log4j-server.properties
@@ -0,0 +1,35 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+# for production, you should probably set pattern to %c instead of %l.  
+# (%l is slower.)
+
+# output messages into a rolling log file as well as stdout
+log4j.rootLogger=INFO,stdout
+
+# stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%5p %d{HH:mm:ss,SSS} %m%n
+
+
+# Application logging options
+#log4j.logger.org.apache.cassandra=DEBUG
+#log4j.logger.org.apache.cassandra.db=DEBUG
+#log4j.logger.org.apache.cassandra.service.StorageProxy=DEBUG
+
+# Adding this to avoid thrift logging disconnect errors.
+log4j.logger.org.apache.thrift.server.TNonblockingServer=ERROR

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/testutils/src/main/resources/log4j.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/testutils/src/main/resources/log4j.xml b/stack/corepersistence/testutils/src/main/resources/log4j.xml
new file mode 100644
index 0000000..2db8103
--- /dev/null
+++ b/stack/corepersistence/testutils/src/main/resources/log4j.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+    <appender class="org.apache.log4j.ConsoleAppender" name="stdout">
+        <layout class="org.apache.log4j.PatternLayout">
+            <param value="%d %p (%t) [%c] - %m%n" name="ConversionPattern"/>
+        </layout>
+    </appender>
+    <logger name="org.apache">
+        <level value="info"/>
+    </logger>
+    <root>
+        <level value="debug"/>
+        <appender-ref ref="stdout"/>
+    </root>
+</log4j:configuration>


[2/3] Initial import of 2.0 core persistence code.

Posted by to...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImpl.java
new file mode 100644
index 0000000..f6217a2
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImpl.java
@@ -0,0 +1,219 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.cassandra.db.marshal.ReversedType;
+import org.apache.cassandra.db.marshal.UUIDType;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.migration.CollectionColumnFamily;
+import org.apache.usergrid.persistence.collection.migration.Migration;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccLogEntry;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccLogEntryImpl;
+import org.apache.usergrid.persistence.collection.mvcc.entity.Stage;
+
+import com.google.common.base.Preconditions;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import com.netflix.astyanax.ColumnListMutation;
+import com.netflix.astyanax.Keyspace;
+import com.netflix.astyanax.MutationBatch;
+import com.netflix.astyanax.connectionpool.OperationResult;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+import com.netflix.astyanax.connectionpool.exceptions.NotFoundException;
+import com.netflix.astyanax.model.Column;
+import com.netflix.astyanax.model.ColumnFamily;
+import com.netflix.astyanax.serializers.UUIDSerializer;
+
+
+/**
+ * Simple implementation for reading and writing log entries
+ *
+ * @author tnine
+ */
+@Singleton
+public class MvccLogEntrySerializationStrategyImpl implements MvccLogEntrySerializationStrategy, Migration {
+
+    public static final String TIMEOUT_PROP = "collection.stage.transient.timeout";
+
+    private static final ColumnFamily<UUID, UUID> CF_ENTITY_LOG =
+            new ColumnFamily<UUID, UUID>( "Entity_Log", UUIDSerializer.get(), UUIDSerializer.get() );
+
+    /**
+     * Used for caching the byte => stage mapping
+     */
+    private static final StageCache CACHE = new StageCache();
+
+
+    protected final Keyspace keyspace;
+    protected final int timeout;
+
+
+    @Inject
+    public MvccLogEntrySerializationStrategyImpl( final Keyspace keyspace, @Named( TIMEOUT_PROP ) final int timeout ) {
+        this.keyspace = keyspace;
+        this.timeout = timeout;
+    }
+
+
+    @Override
+    public MutationBatch write( final MvccLogEntry entry ) {
+
+        Preconditions.checkNotNull( entry, "entry is required" );
+
+
+        final Stage stage = entry.getStage();
+        final UUID colName = entry.getVersion();
+        final byte colValue = stage.getId();
+
+        return doWrite( entry.getContext(), entry.getEntityId(), new RowOp() {
+            @Override
+            public void doOp( final ColumnListMutation<UUID> colMutation ) {
+
+                //Write the stage with a timeout, it's set as transient
+                if ( stage.isTransient() ) {
+                    colMutation.putColumn( colName, colValue, timeout );
+                    return;
+                }
+
+                //otherwise it's persistent, write it with no expiration
+                colMutation.putColumn( colName, colValue );
+            }
+        } );
+    }
+
+
+    @Override
+    public MvccLogEntry load( final CollectionContext context, final UUID entityId, final UUID version )
+            throws ConnectionException {
+        Preconditions.checkNotNull( context, "context is required" );
+        Preconditions.checkNotNull( entityId, "entity id is required" );
+        Preconditions.checkNotNull( version, "version is required" );
+
+
+        Column<UUID> result = null;
+
+        try {
+            OperationResult<Column<UUID>>
+                    foo = keyspace.prepareQuery( CF_ENTITY_LOG ).getKey( entityId ).getColumn( version ).execute();
+
+            result = foo.getResult();
+        }
+        catch ( NotFoundException nfe ) {
+            return null;
+        }
+
+        if ( result == null ) {
+            return null;
+        }
+
+        final byte stored = result.getByteValue();
+
+
+        final Stage stage = CACHE.getStage( stored );
+
+        Preconditions.checkNotNull( "No stage was found for byte value " + stored + ".  This is a migration data bug" );
+
+        return new MvccLogEntryImpl( context, entityId, version, stage );
+    }
+
+
+    @Override
+    public List<MvccLogEntry> load( final CollectionContext context, final UUID entityId, final UUID version,
+                                    final int maxSize ) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+
+    @Override
+    public MutationBatch delete( final CollectionContext context, final UUID entityId, final UUID version ) {
+
+        Preconditions.checkNotNull( context, "context is required" );
+        Preconditions.checkNotNull( entityId, "entityId is required" );
+        Preconditions.checkNotNull( version, "version context is required" );
+
+        return doWrite( context, entityId, new RowOp() {
+            @Override
+            public void doOp( final ColumnListMutation<UUID> colMutation ) {
+                colMutation.deleteColumn( version );
+            }
+        } );
+    }
+
+
+    @Override
+    public Collection<CollectionColumnFamily> getColumnFamilies() {
+        //create the CF entity data.  We want it reversed b/c we want the most recent version at the top of the
+        //row for fast seeks
+        CollectionColumnFamily cf = new CollectionColumnFamily( CF_ENTITY_LOG,
+                ReversedType.class.getName() + "(" + UUIDType.class.getName() + ")", true );
+
+
+        return Collections.singleton( cf );
+    }
+
+
+    /**
+     * Simple callback to perform puts and deletes with a common row setup code
+     */
+    private static interface RowOp {
+
+        /**
+         * The operation to perform on the row
+         */
+        void doOp( ColumnListMutation<UUID> colMutation );
+    }
+
+
+    /**
+     * Do the column update or delete for the given column and row key
+     *
+     * @param context We need to use this when getting the keyspace
+     */
+    private MutationBatch doWrite( CollectionContext context, UUID entityId, RowOp op ) {
+
+        final MutationBatch batch = keyspace.prepareMutationBatch();
+
+        op.doOp( batch.withRow( CF_ENTITY_LOG, entityId ) );
+
+        return batch;
+    }
+
+
+    /**
+     * Internal stage cache
+     */
+    private static class StageCache {
+        private Map<Byte, Stage> values = new HashMap<Byte, Stage>( Stage.values().length );
+
+
+        private StageCache() {
+            for ( Stage stage : Stage.values() ) {
+
+                final byte stageValue = stage.getId();
+
+                if ( values.containsKey( stageValue ) ) {
+                    throw new RuntimeException(
+                            "There are two Stages assigned to the byte " + stageValue + ".  This is a bug" );
+                }
+
+                values.put( stageValue, stage );
+            }
+        }
+
+
+        /**
+         * Get the stage with the byte value
+         */
+        private Stage getStage( final byte value ) {
+            return values.get( value );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryTest.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryTest.java
new file mode 100644
index 0000000..4450efe
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryTest.java
@@ -0,0 +1,8 @@
+package org.apache.usergrid.persistence.collection;
+
+
+/**
+ * Basic tests
+ * @author tnine
+ */
+public class CollectionManagerFactoryTest {}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/guice/TestCollectionModule.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/guice/TestCollectionModule.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/guice/TestCollectionModule.java
new file mode 100644
index 0000000..3ea6e62
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/guice/TestCollectionModule.java
@@ -0,0 +1,103 @@
+package org.apache.usergrid.persistence.collection.guice;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.cassandra.locator.SimpleStrategy;
+
+import org.apache.usergrid.persistence.collection.astynax.AstynaxKeyspaceProvider;
+import org.apache.usergrid.persistence.collection.migration.MigrationException;
+import org.apache.usergrid.persistence.collection.migration.MigrationManager;
+import org.apache.usergrid.persistence.collection.migration.MigrationManagerImpl;
+import org.apache.usergrid.persistence.collection.serialization.MvccLogEntrySerializationStrategyImpl;
+import org.apache.usergrid.persistence.test.CassandraRule;
+
+import com.google.guiceberry.GuiceBerryEnvMain;
+import com.google.guiceberry.GuiceBerryModule;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.name.Names;
+
+
+/**
+ * Simple module for wiring our collection api
+ *
+ * @author tnine
+ */
+public class TestCollectionModule extends AbstractModule {
+
+
+    public TestCollectionModule() {
+    }
+
+
+    @Override
+    protected void configure() {
+
+
+        //import the guice berry module
+        install( new GuiceBerryModule() );
+
+        //now configure our db
+        bind( GuiceBerryEnvMain.class ).to( CassAppMain.class );
+
+        //import the runtime module
+        install( new CollectionModule() );
+
+
+        //configure our integration test properties. This should remain the same across all tests
+
+        Map<String, String> configProperties = new HashMap<String, String>();
+        configProperties.put( AstynaxKeyspaceProvider.CASSANDRA_HOSTS, "localhost" );
+        configProperties.put( AstynaxKeyspaceProvider.CASSANDRA_PORT, "" + CassandraRule.THRIFT_PORT );
+        configProperties.put( AstynaxKeyspaceProvider.CASSANDRA_CONNECTIONS, "10" );
+        configProperties.put( AstynaxKeyspaceProvider.CASSANDRA_CLUSTER_NAME, "Usergrid" );
+        configProperties.put( AstynaxKeyspaceProvider.CASSANDRA_VERSION, "1.2" );
+        configProperties.put( AstynaxKeyspaceProvider.COLLECTIONS_KEYSPACE_NAME, "Usergrid_Collections" );
+
+        configProperties.put( MigrationManagerImpl.REPLICATION_FACTOR, "1" );
+        configProperties.put( MigrationManagerImpl.STRATEGY_CLASS, SimpleStrategy.class.getName() );
+
+        /**
+         * Set the timeout to 60 seconds, no test should take that long for load+delete without a failure
+         */
+        configProperties.put( MvccLogEntrySerializationStrategyImpl.TIMEOUT_PROP, 60+"" );
+
+        Map<String, String> props = getOverrides();
+
+        if(props != null){
+            configProperties.putAll( props );
+        }
+
+        //bind to the props
+        Names.bindProperties( binder(), configProperties );
+    }
+
+
+    /**
+     * Get any overrides we need for system properties
+     */
+    public Map<String, String> getOverrides() {
+        return null;
+    }
+
+
+    static class CassAppMain implements GuiceBerryEnvMain {
+
+        @Inject
+        protected MigrationManager migrationManager;
+
+
+        public void run() {
+            try {
+                //run the injected migration manager to set up cassandra
+                migrationManager.migrate();
+            }
+            catch ( MigrationException e ) {
+                throw new RuntimeException( e );
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/mvcc/entity/StageTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/mvcc/entity/StageTest.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/mvcc/entity/StageTest.java
new file mode 100644
index 0000000..0090e64
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/mvcc/entity/StageTest.java
@@ -0,0 +1,91 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author tnine
+ */
+public class StageTest {
+
+    @Test
+    public void active() {
+
+        assertTrue( Stage.ACTIVE.isTransient() );
+
+        assertEquals( ( byte ) 0, Stage.ACTIVE.getId() );
+
+        testUnique( Stage.ACTIVE );
+    }
+
+
+    @Test
+    public void rollback() {
+
+        assertTrue( Stage.ROLLBACK.isTransient() );
+
+        assertEquals( ( byte ) 1, Stage.ROLLBACK.getId() );
+
+        testUnique( Stage.ROLLBACK );
+    }
+
+
+    @Test
+    public void comitted() {
+
+        assertFalse( Stage.COMMITTED.isTransient() );
+
+        assertEquals( ( byte ) 2, Stage.COMMITTED.getId() );
+
+        testUnique( Stage.COMMITTED );
+    }
+
+
+
+
+    @Test
+    public void postProcess() {
+
+        assertFalse( Stage.POSTPROCESS.isTransient() );
+
+        assertEquals( ( byte ) 6, Stage.POSTPROCESS.getId() );
+
+        testUnique( Stage.POSTPROCESS );
+    }
+
+
+    @Test
+    public void complete() {
+
+        assertFalse( Stage.COMPLETE.isTransient() );
+
+        assertEquals( ( byte ) 14, Stage.COMPLETE.getId() );
+
+        testUnique( Stage.COMPLETE );
+    }
+
+
+    /**
+     * Test we don't have dups in the byte value
+     * @param test
+     */
+    private void testUnique( Stage test ) {
+
+        for ( Stage stage : Stage.values() ) {
+
+            //skip self
+            if ( stage == test ) {
+                continue;
+            }
+
+            assertFalse( stage.getId() == test.getId() );
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImplTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImplTest.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImplTest.java
new file mode 100644
index 0000000..a3bcead
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImplTest.java
@@ -0,0 +1,343 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.cassandra.db.marshal.UUIDType;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.CollectionContextImpl;
+import org.apache.usergrid.persistence.collection.guice.TestCollectionModule;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntityImpl;
+import org.apache.usergrid.persistence.model.entity.Entity;
+import org.apache.usergrid.persistence.model.field.BooleanField;
+import org.apache.usergrid.persistence.model.field.DoubleField;
+import org.apache.usergrid.persistence.model.field.Field;
+import org.apache.usergrid.persistence.model.field.IntegerField;
+import org.apache.usergrid.persistence.model.field.LongField;
+import org.apache.usergrid.persistence.model.field.StringField;
+import org.apache.usergrid.persistence.model.field.UUIDField;
+import org.apache.usergrid.persistence.model.util.UUIDGenerator;
+import org.apache.usergrid.persistence.test.CassandraRule;
+
+import com.google.common.base.Optional;
+import com.google.guiceberry.junit4.GuiceBerryRule;
+import com.google.inject.Inject;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+import com.netflix.astyanax.serializers.UUIDSerializer;
+import com.netflix.astyanax.util.TimeUUIDUtils;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNull;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * @author tnine
+ */
+public class MvccEntitySerializationStrategyImplTest {
+
+    @Rule
+    public final GuiceBerryRule guiceBerry = new GuiceBerryRule( TestCollectionModule.class );
+
+    @Rule
+    public final CassandraRule rule = new CassandraRule();
+
+    @Inject
+    private MvccEntitySerializationStrategy serializationStrategy;
+
+
+    @Test
+    public void writeLoadDelete() throws ConnectionException {
+
+        final UUID applicationId = UUIDGenerator.newTimeUUID();
+        final String name = "test";
+
+        CollectionContext context = new CollectionContextImpl( applicationId, applicationId, name );
+
+
+        final UUID entityId = UUIDGenerator.newTimeUUID();
+        final UUID version = UUIDGenerator.newTimeUUID();
+        final String type = "test";
+        final long created = 1l;
+        final long updated = 2l;
+
+        Entity entity = new Entity( entityId, type );
+
+        entity.setVersion( version );
+        entity.setCreated( created );
+        entity.setUpdated( updated );
+
+
+        BooleanField boolField = new BooleanField( "boolean", false );
+        DoubleField doubleField = new DoubleField( "double", 1d );
+        IntegerField intField = new IntegerField( "long", 1 );
+        LongField longField = new LongField( "int", 1l );
+        StringField stringField = new StringField( "name", "test" );
+        UUIDField uuidField = new UUIDField( "uuid", UUIDGenerator.newTimeUUID() );
+
+        entity.setField( boolField );
+        entity.setField( doubleField );
+        entity.setField( intField );
+        entity.setField( longField );
+        entity.setField( stringField );
+        entity.setField( uuidField );
+
+
+        MvccEntity saved = new MvccEntityImpl( context, entityId, version, Optional.of( entity ) );
+
+
+        //persist the entity
+        serializationStrategy.write( saved ).execute();
+
+        //now read it back
+
+        MvccEntity returned = serializationStrategy.load( context, entityId, version );
+
+        assertEquals( "Mvcc entities are the same", saved, returned );
+
+
+        assertEquals( entityId, entity.getUuid() );
+        assertEquals( type, entity.getType() );
+        assertEquals( created, entity.getCreated() );
+        assertEquals( updated, entity.getUpdated() );
+
+
+        Field<Boolean> boolFieldReturned = entity.getField( boolField.getName() );
+
+        assertSame( boolField, boolFieldReturned );
+
+        Field<Double> doubleFieldReturned = entity.getField( doubleField.getName() );
+
+        assertSame( doubleField, doubleFieldReturned );
+
+        Field<Integer> intFieldReturned = entity.getField( intField.getName() );
+
+        assertSame( intField, intFieldReturned );
+
+        Field<Long> longFieldReturned = entity.getField( longField.getName() );
+
+        assertSame( longField, longFieldReturned );
+
+        Field<String> stringFieldReturned = entity.getField( stringField.getName() );
+
+        assertSame( stringField, stringFieldReturned );
+
+        Field<UUID> uuidFieldReturned = entity.getField( uuidField.getName() );
+
+        assertSame( uuidField, uuidFieldReturned );
+
+
+        Set<Field> results = new HashSet<Field>();
+        results.addAll( entity.getFields() );
+
+
+        assertTrue( results.contains( boolField ) );
+        assertTrue( results.contains( doubleField ) );
+        assertTrue( results.contains( intField ) );
+        assertTrue( results.contains( longField ) );
+        assertTrue( results.contains( stringField ) );
+        assertTrue( results.contains( uuidField ) );
+
+        assertEquals( 6, results.size() );
+
+
+        assertEquals( entityId, entity.getUuid() );
+        assertEquals( version, entity.getVersion() );
+
+
+        //now delete it
+        serializationStrategy.delete( context, entityId, version ).execute();
+
+        //now get it, should be gone
+
+        returned = serializationStrategy.load( context, entityId, version );
+
+        assertNull( returned );
+    }
+
+
+    @Test
+    public void writeLoadClearDelete() throws ConnectionException {
+
+        final UUID applicationId = UUIDGenerator.newTimeUUID();
+        final String name = "test";
+
+        CollectionContext context = new CollectionContextImpl( applicationId, applicationId, name );
+
+
+        final UUID entityId = UUIDGenerator.newTimeUUID();
+        final UUID version = UUIDGenerator.newTimeUUID();
+        final String type = "test";
+        final long created = 1l;
+        final long updated = 2l;
+
+        Entity entity = new Entity( entityId, type );
+
+        entity.setVersion( version );
+        entity.setCreated( created );
+        entity.setUpdated( updated );
+
+
+        MvccEntity saved = new MvccEntityImpl( context, entityId, version, Optional.of( entity ) );
+
+
+        //persist the entity
+        serializationStrategy.write( saved ).execute();
+
+        //now read it back
+
+        MvccEntity returned = serializationStrategy.load( context, entityId, version );
+
+        assertEquals( "Mvcc entities are the same", saved, returned );
+
+
+        assertEquals( entityId, entity.getUuid() );
+        assertEquals( type, entity.getType() );
+        assertEquals( created, entity.getCreated() );
+        assertEquals( updated, entity.getUpdated() );
+
+
+        //now clear it
+
+        serializationStrategy.clear( context, entityId, version ).execute();
+
+        returned = serializationStrategy.load( context, entityId, version );
+
+        assertEquals( context, returned.getContext() );
+        assertEquals( entityId, returned.getUuid() );
+        assertEquals( version, returned.getVersion() );
+        assertFalse( returned.getEntity().isPresent() );
+
+        //now delete it
+        serializationStrategy.delete( context, entityId, version ).execute();
+
+        //now get it, should be gone
+
+        returned = serializationStrategy.load( context, entityId, version );
+
+        assertNull( returned );
+    }
+
+
+    @Test
+    public void writeX2ClearDelete() throws ConnectionException {
+
+        final UUID applicationId = UUIDGenerator.newTimeUUID();
+        final String name = "test";
+
+        CollectionContext context = new CollectionContextImpl( applicationId, applicationId, name );
+
+
+        final UUID entityId = UUIDGenerator.newTimeUUID();
+        final UUID version1 = UUIDGenerator.newTimeUUID();
+        final String type = "test";
+
+        Entity entityv1 = new Entity( entityId, type );
+
+        entityv1.setVersion( version1 );
+
+
+        MvccEntity saved = new MvccEntityImpl( context, entityId, version1, Optional.of( entityv1 ) );
+
+
+        //persist the entity
+        serializationStrategy.write( saved ).execute();
+
+        //now read it back
+
+        MvccEntity returnedV1 = serializationStrategy.load( context, entityId, version1 );
+
+        assertEquals( "Mvcc entities are the same", saved, returnedV1 );
+
+
+        assertEquals( entityId, entityv1.getUuid() );
+        assertEquals( type, entityv1.getType() );
+
+
+        //now write a new version of it
+
+
+        Entity entityv2 = new Entity( entityId, type );
+
+        UUID version2 = UUIDGenerator.newTimeUUID();
+        entityv2.setVersion( version2 );
+
+
+        UUIDType comparator = UUIDType.instance;
+
+        int value = comparator.compare( UUIDSerializer.get().toByteBuffer( version1 ), UUIDSerializer.get().toByteBuffer( version2 ) );
+
+        assertTrue(value < 0);
+
+        value = comparator.compare( UUIDSerializer.get().toByteBuffer( version2 ), UUIDSerializer.get().toByteBuffer( version2 ) );
+
+        assertEquals(0, value);
+
+        MvccEntity savedV2 = new MvccEntityImpl( context, entityId, version2, Optional.of( entityv2 ) );
+
+        serializationStrategy.write( savedV2 ).execute();
+
+        MvccEntity returnedV2 = serializationStrategy.load( context, entityId, version2 );
+
+        assertEquals( "Mvcc entities are the same", savedV2, returnedV2 );
+
+
+        //now clear it at v3
+
+        UUID version3 = UUIDGenerator.newTimeUUID();
+
+        serializationStrategy.clear( context, entityId, version3 ).execute();
+
+
+        final Optional<Entity> empty = Optional.absent();
+
+        MvccEntity clearedV3 = new MvccEntityImpl( context, entityId, version3, empty );
+
+        MvccEntity returnedV3 = serializationStrategy.load( context, entityId, version3 );
+
+        assertEquals("entities are the same", clearedV3, returnedV3);
+
+        //now ask for up to 10 versions from the current version, we should get cleared, v2, v1
+        UUID current = UUIDGenerator.newTimeUUID();
+
+        List<MvccEntity> entities = serializationStrategy.load( context, entityId, current, 3 );
+
+        assertEquals( 3, entities.size() );
+
+        assertEquals( clearedV3, entities.get( 0 ) );
+
+        assertEquals( returnedV2, entities.get( 1 ) );
+
+        assertEquals( returnedV1, entities.get( 2 ) );
+
+
+        //now delete v2 and v1, we should still get v3
+        serializationStrategy.delete( context, entityId, version1 ).execute();
+        serializationStrategy.delete( context, entityId, version2 ).execute();
+
+        entities = serializationStrategy.load( context, entityId, current, 3 );
+
+        assertEquals( 1, entities.size() );
+
+        assertEquals( clearedV3, entities.get( 0 ) );
+
+
+        //now get it, should be gone
+        serializationStrategy.delete( context, entityId, version3 ).execute();
+
+
+        entities = serializationStrategy.load( context, entityId, current, 3 );
+
+        assertEquals( 0, entities.size() );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImplTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImplTest.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImplTest.java
new file mode 100644
index 0000000..c66fac2
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategyImplTest.java
@@ -0,0 +1,150 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.CollectionContextImpl;
+import org.apache.usergrid.persistence.collection.guice.TestCollectionModule;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccLogEntry;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccLogEntryImpl;
+import org.apache.usergrid.persistence.collection.mvcc.entity.Stage;
+import org.apache.usergrid.persistence.model.util.UUIDGenerator;
+import org.apache.usergrid.persistence.test.CassandraRule;
+
+import com.google.guiceberry.GuiceBerryEnvSelector;
+import com.google.guiceberry.TestDescription;
+import com.google.guiceberry.junit4.GuiceBerryRule;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+
+import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+
+/**
+ * @author tnine
+ */
+public class MvccLogEntrySerializationStrategyImplTest {
+
+
+    /**
+     * Set our timeout to 1 seconds.  If it works for 1 seconds, we'll be good a any value
+     */
+    private static final int TIMEOUT = 1;
+
+
+    @Rule
+    public final GuiceBerryRule guiceBerry = new GuiceBerryRule( new TimeoutModMapper() );
+
+    @Rule
+    public final CassandraRule rule = new CassandraRule();
+
+    @Inject
+    private MvccLogEntrySerializationStrategy logEntryStrategy;
+
+
+    @Test
+    public void createAndDelete() throws ConnectionException {
+
+        final UUID applicationId = UUIDGenerator.newTimeUUID();
+        final String name = "test";
+
+
+        CollectionContext context = new CollectionContextImpl( applicationId, applicationId, name );
+
+
+        final UUID uuid = UUIDGenerator.newTimeUUID();
+        final UUID version = UUIDGenerator.newTimeUUID();
+
+        for ( Stage stage : Stage.values() ) {
+            MvccLogEntry saved = new MvccLogEntryImpl( context, uuid, version, stage );
+            logEntryStrategy.write( saved ).execute();
+
+            //Read it back
+
+            MvccLogEntry returned = logEntryStrategy.load( context, uuid, version );
+
+            assertNotNull( "Returned value should not be null", returned );
+
+            assertEquals( "Returned should equal the saved", saved, returned );
+        }
+    }
+
+
+    @Test
+    public void transientTimeout() throws ConnectionException, InterruptedException {
+
+        final UUID applicationId = UUIDGenerator.newTimeUUID();
+        final String name = "test";
+
+
+        CollectionContext context = new CollectionContextImpl( applicationId, applicationId, name );
+
+
+        final UUID uuid = UUIDGenerator.newTimeUUID();
+        final UUID version = UUIDGenerator.newTimeUUID();
+
+        for ( Stage stage : Stage.values() ) {
+
+            MvccLogEntry saved = new MvccLogEntryImpl( context, uuid, version, stage );
+            logEntryStrategy.write( saved ).execute();
+
+            //Read it back after the timeout
+
+            Thread.sleep( TIMEOUT * 1000 );
+
+            MvccLogEntry returned = logEntryStrategy.load( context, uuid, version );
+
+
+            if ( stage.isTransient() ) {
+
+                assertNull( "Active is transient and should time out", returned );
+            }
+            else {
+                assertNotNull( "Committed is not transient and should be returned", returned );
+
+                assertEquals( "Returned should equal the saved", saved, returned );
+            }
+        }
+    }
+
+
+    /**
+     * Mapper that will change which module we implement based on the test case
+     */
+    public static class TimeoutModMapper implements GuiceBerryEnvSelector {
+
+        @Override
+        public Class<? extends Module> guiceBerryEnvToUse( final TestDescription testDescription ) {
+
+            //in this edge case, we want to truncate the timeout to 1 second for this test, override the env to use
+            //this module setup
+            if ( (MvccLogEntrySerializationStrategyImplTest.class.getName()+".transientTimeout").equals( testDescription.getName() ) ) {
+                return TimeoutEnv.class;
+            }
+
+            //by default, we wnat to run the TestCollectionModule
+            return TestCollectionModule.class;
+        }
+    }
+
+
+    public static class TimeoutEnv extends TestCollectionModule {
+
+        @Override
+        public Map<String, String> getOverrides() {
+            Map<String, String> timeout = new HashMap<String, String>();
+            timeout.put( MvccLogEntrySerializationStrategyImpl.TIMEOUT_PROP, TIMEOUT + "" );
+            return timeout;
+        }
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/SerializationComparison.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/SerializationComparison.java b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/SerializationComparison.java
new file mode 100644
index 0000000..a78293b
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/java/org/apache/usergrid/persistence/collection/serialization/SerializationComparison.java
@@ -0,0 +1,182 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.smile.SmileFactory;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.usergrid.persistence.model.entity.Entity;
+import org.apache.usergrid.persistence.model.field.ArrayField;
+import org.apache.usergrid.persistence.model.field.BooleanField;
+import org.apache.usergrid.persistence.model.field.ByteBufferField;
+import org.apache.usergrid.persistence.model.field.DoubleField;
+import org.apache.usergrid.persistence.model.field.EntityObjectField;
+import org.apache.usergrid.persistence.model.field.IntegerField;
+import org.apache.usergrid.persistence.model.field.ListField;
+import org.apache.usergrid.persistence.model.field.LocationField;
+import org.apache.usergrid.persistence.model.field.LongField;
+import org.apache.usergrid.persistence.model.field.SetField;
+import org.apache.usergrid.persistence.model.field.StringField;
+import org.apache.usergrid.persistence.model.field.UUIDField;
+import org.apache.usergrid.persistence.model.util.UUIDGenerator;
+import org.apache.usergrid.persistence.model.value.EntityObject;
+import org.apache.usergrid.persistence.model.value.Location;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.ByteBufferInputStream;
+import com.esotericsoftware.kryo.io.ByteBufferOutputStream;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+
+/**
+ * TODO We need to get both of these serialization methods working, and benchmark them for comparison
+ * Neither works out of the box for us without custom work.
+ * @author tnine
+ */
+public class SerializationComparison {
+
+    private static final Logger logger = LoggerFactory.getLogger( SerializationComparison.class );
+
+    private static final int count = 10000;
+
+
+    @Test
+    @Ignore
+    public void smileSerialization() throws IOException {
+        SmileFactory smile = new SmileFactory();
+
+        ObjectMapper smileMapper = new ObjectMapper( smile );
+
+
+        Entity entity = createEntity();
+
+        long writeTime = 0;
+        long readTime = 0;
+
+        for ( int i = 0; i < count; i++ ) {
+
+            //capture time in nannos for write
+            long writeStart = System.nanoTime();
+
+            byte[] smileData = smileMapper.writeValueAsBytes( entity );
+
+            writeTime += System.nanoTime() - writeStart;
+
+            long readStart = System.nanoTime();
+
+            Entity otherValue = smileMapper.readValue( smileData, Entity.class );
+
+            readTime += System.nanoTime() - readStart;
+        }
+
+        logger.info( "Smile took {} nanos for writing {} entities", writeTime, count );
+        logger.info( "Smile took {} nanos for reading {} entities", readTime, count );
+    }
+
+
+    @Test
+    @Ignore
+    public void kyroSerialization() {
+        Kryo kryo = new Kryo();
+
+        //container classes
+        kryo.register( Entity.class );
+
+        kryo.register( EntityObject.class );
+        kryo.register( Location.class );
+
+
+        //field classes
+        kryo.register( ArrayField.class );
+        kryo.register( BooleanField.class );
+        kryo.register( ByteBufferField.class );
+        kryo.register( DoubleField.class );
+        kryo.register( EntityObjectField.class );
+        kryo.register( IntegerField.class );
+        kryo.register( ListField.class );
+        kryo.register( LocationField.class );
+        kryo.register( LongField.class );
+        kryo.register( SetField.class );
+        kryo.register( StringField.class );
+        kryo.register( UUIDField.class, new de.javakaffee.kryoserializers.UUIDSerializer() );
+
+
+        long writeTime = 0;
+        long readTime = 0;
+
+        for ( int i = 0; i < count; i++ ) {
+
+            //capture time in nanos for write
+            long writeStart = System.nanoTime();
+
+            ByteBuffer data = ByteBuffer.allocate( 1024 );
+            ByteBufferOutputStream byteBuffOutputStream = new ByteBufferOutputStream(data);
+            Output output = new Output( byteBuffOutputStream );
+
+            Entity entity = createEntity();
+
+            kryo.writeObject( output, entity );
+            output.close();
+
+            writeTime += System.nanoTime() - writeStart;
+
+            data.rewind();
+
+            long readStart = System.nanoTime();
+
+
+            Input input = new Input( new ByteBufferInputStream( data ) );
+            Entity loaded = kryo.readObject( input, Entity.class );
+            input.close();
+
+            readTime += System.nanoTime() - readStart;
+        }
+
+        logger.info( "Smile took {} nanos for writing {} entities", writeTime, count );
+        logger.info( "Smile took {} nanos for reading {} entities", readTime, count );
+    }
+
+
+    private Entity createEntity() {
+
+        final UUID entityId = UUIDGenerator.newTimeUUID();
+
+        final UUID version = UUIDGenerator.newTimeUUID();
+
+        Entity entity = new Entity( entityId, "test" );
+        entity.setCreated( 1l );
+        entity.setUpdated( 2l );
+        entity.setVersion( version );
+
+
+        BooleanField boolField = new BooleanField( "boolean", false );
+        DoubleField doubleField = new DoubleField( "double", 1d );
+        IntegerField intField = new IntegerField( "long", 1 );
+        LongField longField = new LongField( "int", 1l );
+        StringField stringField = new StringField( "name", "test" );
+        UUIDField uuidField = new UUIDField( "uuid", UUIDGenerator.newTimeUUID() );
+
+        entity.setField( boolField );
+        entity.setField( doubleField );
+        entity.setField( intField );
+        entity.setField( longField );
+        entity.setField( stringField );
+        entity.setField( uuidField );
+
+        return entity;
+    }
+
+
+}
+
+
+
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/test/resources/cassandra.properties
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/test/resources/cassandra.properties b/stack/corepersistence/collection/src/test/resources/cassandra.properties
new file mode 100644
index 0000000..a69c771
--- /dev/null
+++ b/stack/corepersistence/collection/src/test/resources/cassandra.properties
@@ -0,0 +1,5 @@
+#Purposefully left empty, we override this in our module code to KISS  The runtime requires this file however
+#Boostrapping should fail fast if it's not present
+
+
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/pom.xml b/stack/corepersistence/index/pom.xml
new file mode 100644
index 0000000..85c2a0b
--- /dev/null
+++ b/stack/corepersistence/index/pom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>persistence</artifactId>
+    <groupId>org.apache.usergrid</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>index</artifactId>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.usergrid</groupId>
+      <artifactId>collection</artifactId>
+      <version>${project.parent.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Query.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Query.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Query.java
new file mode 100644
index 0000000..69fdb3f
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Query.java
@@ -0,0 +1,6 @@
+package org.apache.usergrid.persistence.index;
+
+
+/** Interface of our query implementation */
+public interface Query
+{}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngine.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngine.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngine.java
new file mode 100644
index 0000000..631c027
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngine.java
@@ -0,0 +1,22 @@
+package org.apache.usergrid.persistence.index;
+
+
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public interface QueryEngine
+{
+
+
+    /** Search and return the entities */
+    public Results<Entity> search( Query query );
+
+
+    /** Search the query, but parse the entities into the given class We may not need to implement this at first */
+    public <T> Results<T> search( Query query, Class<T> clazz );
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngineFactory.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngineFactory.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngineFactory.java
new file mode 100644
index 0000000..1358a4e
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/QueryEngineFactory.java
@@ -0,0 +1,21 @@
+package org.apache.usergrid.persistence.index;
+
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public interface QueryEngineFactory
+{
+
+    /**
+     * Create an index manager for the collection context
+     *
+     * @param context The context to use when creating the index manager
+     */
+    public QueryEngineFactory createIndexManager( CollectionContext context );
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Results.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Results.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Results.java
new file mode 100644
index 0000000..bc4646e
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/Results.java
@@ -0,0 +1,16 @@
+package org.apache.usergrid.persistence.index;
+
+
+import java.util.Iterator;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public interface Results<Entity> extends Iterable<Entity>, Iterator<Entity>
+{
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Complete.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Complete.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Complete.java
new file mode 100644
index 0000000..da051b6
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Complete.java
@@ -0,0 +1,21 @@
+package org.apache.usergrid.persistence.index.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.collection.mvcc.stage.WriteStage;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class Complete implements WriteStage
+{
+
+    @Override
+    public MvccEntity performStage( final MvccEntity entity ) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Start.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Start.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Start.java
new file mode 100644
index 0000000..a89e2af
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Start.java
@@ -0,0 +1,16 @@
+package org.apache.usergrid.persistence.index.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.collection.mvcc.stage.WriteStage;
+
+
+/** This state should signal an index update has started */
+public class Start implements WriteStage
+{
+
+    @Override
+    public MvccEntity performStage( final MvccEntity entity ) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Write.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Write.java b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Write.java
new file mode 100644
index 0000000..8e75812
--- /dev/null
+++ b/stack/corepersistence/index/src/main/java/org/apache/usergrid/persistence/index/stage/Write.java
@@ -0,0 +1,17 @@
+package org.apache.usergrid.persistence.index.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.collection.mvcc.stage.WriteStage;
+
+
+/** This state should perform an update of the index. */
+public class Write implements WriteStage
+{
+
+    @Override
+    public MvccEntity performStage( final MvccEntity entity ) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/pom.xml b/stack/corepersistence/model/pom.xml
new file mode 100644
index 0000000..a79af0b
--- /dev/null
+++ b/stack/corepersistence/model/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>persistence</artifactId>
+    <groupId>org.apache.usergrid</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>model</artifactId>
+
+  <!-- Runtime Dependencies -->
+  <dependencies>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>15.0</version>
+    </dependency>
+
+    <!-- Time UUID library -->
+    <dependency>
+      <groupId>com.fasterxml.uuid</groupId>
+      <artifactId>java-uuid-generator</artifactId>
+      <version>3.1.3</version>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.11</version>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/Entity.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/Entity.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/Entity.java
new file mode 100644
index 0000000..83cca40
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/entity/Entity.java
@@ -0,0 +1,149 @@
+package org.apache.usergrid.persistence.model.entity;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.model.value.EntityObject;
+
+import com.google.common.base.Preconditions;
+
+
+/**
+ * Simple entity that is used for persistence.  It has 4 required properties. <p/> uuid: The uuid of the entity type:
+ * The entity name (user, car, restaurant etc) created: The time the entity was created in millis since epoch updated:
+ * The time the entity was updated in millis since epoch;
+ */
+public class Entity extends EntityObject {
+
+
+    /**
+     * The entity type. This must be set
+     */
+    private String type;
+
+    /**
+     * The generated uuid.  This should never be set by a user
+     */
+    private UUID uuid;
+
+    /**
+     * The version of this entity.  Options, since it can be used for optimistic locking
+     */
+    private UUID version;
+
+    /**
+     * The time in milliseconds since epoch the entity was created
+     */
+    private long created;
+
+    /**
+     * The time in milliseconds since epoch the entity was updated
+     */
+    private long updated;
+
+
+    /**
+     * Create an entity with no uuid.  This should be used for creating new entities
+     */
+    public Entity( String type ) {
+        Preconditions.checkNotNull( type, "Type must not be null" );
+        Preconditions.checkArgument( type.length() > 0, "Type must have a length" );
+        this.type = type;
+    }
+
+
+    /**
+     * Create an entity with the given type and uuid.  Should be used for all update operations to an existing entity
+     */
+    public Entity( UUID uuid, String type ) {
+        this( type );
+
+        Preconditions.checkNotNull( uuid, "uuid must not be null" );
+
+        this.uuid = uuid;
+    }
+
+
+    /**
+     * Do not use!  This is only for serialization.
+     */
+    public Entity() {
+
+    }
+
+
+    public UUID getUuid() {
+        return uuid;
+    }
+
+
+    public String getType() {
+        return type;
+    }
+
+
+    public UUID getVersion() {
+        return version;
+    }
+
+
+    public void setVersion( final UUID version ) {
+        this.version = version;
+    }
+
+
+    /**
+     * Should only be invoked by the persistence framework
+     */
+    public void setCreated( long created ) {
+        this.created = created;
+    }
+
+
+    /**
+     * Should only be invoked by the persistence framework
+     */
+    public void setUpdated( long updated ) {
+        this.updated = updated;
+    }
+
+
+    public long getCreated() {
+        return created;
+    }
+
+
+    public long getUpdated() {
+        return updated;
+    }
+
+
+    @Override
+    public boolean equals( Object o ) {
+        if ( this == o ) {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() ) {
+            return false;
+        }
+
+        Entity entity = ( Entity ) o;
+
+        if ( type != null ? !type.equals( entity.type ) : entity.type != null ) {
+            return false;
+        }
+        if ( uuid != null ? !uuid.equals( entity.uuid ) : entity.uuid != null ) {
+            return false;
+        }
+
+        return true;
+    }
+
+
+    @Override
+    public int hashCode() {
+        int result = type != null ? type.hashCode() : 0;
+        result = 31 * result + ( uuid != null ? uuid.hashCode() : 0 );
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/AbstractField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/AbstractField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/AbstractField.java
new file mode 100644
index 0000000..674c7a7
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/AbstractField.java
@@ -0,0 +1,85 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import org.apache.usergrid.persistence.model.value.EntityObject;
+
+
+/** Base class for data information */
+public abstract class AbstractField<T> implements Field<T>
+{
+
+
+    /**
+     * Set the object this field belongs to
+     */
+    protected EntityObject parent;
+    protected String name;
+    protected T value;
+
+
+    /**
+     * Name and value must always be present.
+     *
+     * @param name The name of this field
+     * @param value The value to set.   If value is null, this means that the value should be explicitly removed from
+     * the field storage
+     */
+    protected AbstractField( String name, T value )
+    {
+        this.name = name;
+        this.value = value;
+    }
+
+
+    /**
+     * Default constructor for serialization
+     */
+    protected AbstractField(){
+
+    }
+
+
+
+
+    public String getName()
+    {
+        return name;
+    }
+
+
+    @Override
+    public T getValue()
+    {
+        return value;
+    }
+
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+
+        AbstractField that = ( AbstractField ) o;
+
+        if ( !name.equals( that.name ) )
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+
+    @Override
+    public int hashCode()
+    {
+        return name.hashCode();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ArrayField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ArrayField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ArrayField.java
new file mode 100644
index 0000000..cb5f45d
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ArrayField.java
@@ -0,0 +1,27 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/** A marker to signal array handling.  Just delegates to list field for easier handling internally */
+public class ArrayField extends ListField
+{
+
+    /** Contructor that intializes with an empty set for adding to later */
+    public ArrayField( String name )
+    {
+        super(name);
+    }
+
+    public ArrayField(){
+        super();
+    }
+
+    /** Add the value to the list */
+    public void add( Field field )
+    {
+        value.add( field );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/BooleanField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/BooleanField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/BooleanField.java
new file mode 100644
index 0000000..3948b27
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/BooleanField.java
@@ -0,0 +1,19 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class BooleanField extends AbstractField<Boolean> {
+
+    public BooleanField( String name, Boolean value ) {
+        super( name, value );
+    }
+
+
+    public BooleanField() {
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ByteBufferField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ByteBufferField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ByteBufferField.java
new file mode 100644
index 0000000..2dbc193
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ByteBufferField.java
@@ -0,0 +1,29 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.nio.ByteBuffer;
+
+
+/** A field for storing byte buffers */
+public class ByteBufferField extends AbstractField<ByteBuffer>
+{
+
+
+    /** Creates an immutable copy of the byte buffer */
+    public ByteBufferField( String name, ByteBuffer value )
+    {
+        //always return a duplicate so we don't mess with the markers
+        super( name, value.duplicate() );
+    }
+
+    public ByteBufferField() {
+
+        }
+
+    @Override
+    public ByteBuffer getValue()
+    {
+        //always return a duplicate so we don't mess with the markers
+        return value.duplicate();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DoubleField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DoubleField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DoubleField.java
new file mode 100644
index 0000000..729560c
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/DoubleField.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class DoubleField extends AbstractField<Double>
+{
+
+    public DoubleField( String name, Double value )
+    {
+        super( name, value );
+    }
+
+    public DoubleField() {
+
+        }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/EntityObjectField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/EntityObjectField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/EntityObjectField.java
new file mode 100644
index 0000000..4031a81
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/EntityObjectField.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import org.apache.usergrid.persistence.model.value.EntityObject;
+
+
+/** An object field */
+public class EntityObjectField extends AbstractField<EntityObject>
+{
+
+
+    public EntityObjectField( String name, EntityObject value )
+    {
+        super( name, value );
+    }
+
+    public EntityObjectField() {
+
+        }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/Field.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/Field.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/Field.java
new file mode 100644
index 0000000..5061d49
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/Field.java
@@ -0,0 +1,28 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.io.Serializable;
+
+import org.apache.usergrid.persistence.model.value.EntityObject;
+
+
+/**
+ * Interface for fields.  All fields must implement this method The T is the type of field (in the java runtime) The V
+ * is the value of the field
+ */
+public interface Field<T> extends Serializable {
+
+    /**
+     * Get the name of the field
+     * @return
+     */
+    public String getName();
+
+    /**
+     * Get the value of the field
+     * @return
+     */
+    public T getValue();
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/IntegerField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/IntegerField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/IntegerField.java
new file mode 100644
index 0000000..9c807e0
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/IntegerField.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class IntegerField extends AbstractField<Integer>
+{
+
+    public IntegerField( String name, Integer value )
+    {
+        super( name, value );
+    }
+
+    public IntegerField() {
+
+        }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ListField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ListField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ListField.java
new file mode 100644
index 0000000..0795201
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/ListField.java
@@ -0,0 +1,28 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/** An object field that represents a list of objects.  This can also be used to represent arrays */
+public class ListField extends AbstractField<List<Field>>
+{
+
+    /** Contructor that intializes with an empty set for adding to later */
+    public ListField( String name )
+    {
+        super( name, new ArrayList<Field>() );
+    }
+
+    public ListField(){
+        super();
+    }
+
+
+    /** Add the value to the list */
+    public void add( Field field )
+    {
+        value.add( field );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LocationField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LocationField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LocationField.java
new file mode 100644
index 0000000..5d9fd57
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LocationField.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import org.apache.usergrid.persistence.model.value.Location;
+
+
+/** Basic field for storing location data */
+public class LocationField extends AbstractField<Location>
+{
+
+    /** Create a location field with the given point */
+    public LocationField( String name, Location value )
+    {
+        super( name, value );
+    }
+
+    public LocationField() {
+
+        }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LongField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LongField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LongField.java
new file mode 100644
index 0000000..c56b514
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/LongField.java
@@ -0,0 +1,23 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class LongField extends AbstractField<Long>
+{
+
+    public LongField( String name, Long value )
+    {
+        super( name, value );
+    }
+
+    public LongField() {
+
+        }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/SetField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/SetField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/SetField.java
new file mode 100644
index 0000000..f28b5ec
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/SetField.java
@@ -0,0 +1,28 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+
+/** An object field that represents a set of objects */
+public class SetField extends AbstractField<Set<Field>>
+{
+
+
+    /** Contructor that intializes with an empty set for adding to later */
+    public SetField( String name )
+    {
+        super( name, new LinkedHashSet<Field>() );
+    }
+
+    public SetField() {
+
+        }
+
+    /** Add an entry to the set */
+    public void addEntry( Field field )
+    {
+        value.add( field );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/StringField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/StringField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/StringField.java
new file mode 100644
index 0000000..bcb8b2d
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/StringField.java
@@ -0,0 +1,17 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+/** A String field */
+public class StringField extends AbstractField<String>
+{
+
+
+    public StringField( String name, String value )
+    {
+        super( name, value );
+    }
+
+    public StringField() {
+
+        }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/UUIDField.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/UUIDField.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/UUIDField.java
new file mode 100644
index 0000000..11dfc22
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/field/UUIDField.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.util.UUID;
+
+
+/** A String field */
+public class UUIDField extends AbstractField<UUID>
+{
+
+
+    public UUIDField( String name, UUID value )
+    {
+        super( name, value );
+    }
+
+    public UUIDField() {
+
+        }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/util/UUIDGenerator.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/util/UUIDGenerator.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/util/UUIDGenerator.java
new file mode 100644
index 0000000..687e27b
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/util/UUIDGenerator.java
@@ -0,0 +1,106 @@
+package org.apache.usergrid.persistence.model.util;
+
+
+import java.io.IOException;
+import java.util.Random;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.fasterxml.uuid.EthernetAddress;
+import com.fasterxml.uuid.TimestampSynchronizer;
+import com.fasterxml.uuid.UUIDTimer;
+import com.fasterxml.uuid.impl.TimeBasedGenerator;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public class UUIDGenerator
+{
+
+
+    private static final TimestampSynchronizer synchronizer = new TimestampSynchronizer()
+    {
+
+        /**
+         * Pointer to the last value we returned
+         */
+        private long last = 0;
+
+        /**
+         * The number of ticks that can be used in the millisecond.  In a time UUID a tick is divided into 1/10000 of
+         * a millisecond
+         *
+         */
+        private AtomicInteger ticks = new AtomicInteger();
+
+
+        @Override
+        protected long initialize() throws IOException
+        {
+
+            last = System.currentTimeMillis();
+            return last;
+        }
+
+
+        @Override
+        protected void deactivate() throws IOException
+        {
+            //no op
+        }
+
+
+        @Override
+        protected long update( long now ) throws IOException
+        {
+            /**
+             * It's greater
+             */
+            if ( now > last )
+            {
+                last = now;
+                ticks.set( 0 );
+                return last;
+            }
+
+            //we have the same value (since now should always be increasing) increment a tick
+            last = now + ticks.incrementAndGet();
+
+            return last;
+        }
+    };
+
+
+    private static final Random random = new Random();
+    private static final UUIDTimer timer;
+
+
+    /**
+     * Lame, but required
+     */
+    static
+    {
+        try
+        {
+            timer = new UUIDTimer( random, synchronizer );
+        }
+        catch ( IOException e )
+        {
+            throw new RuntimeException( "Couldn't intialize timer", e );
+        }
+    }
+
+
+    private static final TimeBasedGenerator generator =
+            new TimeBasedGenerator( EthernetAddress.fromInterface(), timer );
+
+
+    /** Create a new time uuid */
+    public static UUID newTimeUUID()
+    {
+        return generator.generate();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/EntityObject.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/EntityObject.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/EntityObject.java
new file mode 100644
index 0000000..5f02053
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/EntityObject.java
@@ -0,0 +1,40 @@
+package org.apache.usergrid.persistence.model.value;
+
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.usergrid.persistence.model.field.Field;
+
+
+/** Simple wrapper for holding nested objects */
+public class EntityObject implements Serializable
+{
+
+
+    /** Fields the users can set */
+    private Map<String, Field> fields = new HashMap<String, Field>();
+
+
+    /** Add the field, return the old one if it existed */
+    public <T extends java.lang.Object> Field<T> setField( Field<T> value )
+    {
+        return fields.put( value.getName(), value );
+    }
+
+
+    /** Get the field by name the user has set into the entity */
+    public <T extends java.lang.Object> Field<T> getField( String name )
+    {
+        return fields.get( name );
+    }
+
+
+    /** Get all fields in the entity */
+    public Collection<Field> getFields()
+    {
+        return fields.values();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/Location.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/Location.java b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/Location.java
new file mode 100644
index 0000000..4370aa1
--- /dev/null
+++ b/stack/corepersistence/model/src/main/java/org/apache/usergrid/persistence/model/value/Location.java
@@ -0,0 +1,32 @@
+package org.apache.usergrid.persistence.model.value;
+
+
+import java.io.Serializable;
+
+
+/** Geographic point.  Should be used when we want to store geo information */
+public class Location  implements Serializable
+{
+
+    private final double latitude;
+    private final double longtitude;
+
+
+    public Location( double latitude, double longtitude )
+    {
+        this.latitude = latitude;
+        this.longtitude = longtitude;
+    }
+
+
+    public double getLatitude()
+    {
+        return latitude;
+    }
+
+
+    public double getLongtitude()
+    {
+        return longtitude;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/model/src/test/java/org/apache/usergrid/persistence/model/field/EntityTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/model/src/test/java/org/apache/usergrid/persistence/model/field/EntityTest.java b/stack/corepersistence/model/src/test/java/org/apache/usergrid/persistence/model/field/EntityTest.java
new file mode 100644
index 0000000..9f94b18
--- /dev/null
+++ b/stack/corepersistence/model/src/test/java/org/apache/usergrid/persistence/model/field/EntityTest.java
@@ -0,0 +1,117 @@
+package org.apache.usergrid.persistence.model.field;
+
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+import org.junit.Test;
+
+import org.apache.usergrid.persistence.model.entity.Entity;
+import org.apache.usergrid.persistence.model.util.UUIDGenerator;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+
+/** Simple test for validating stored entities */
+public class EntityTest
+{
+
+    @Test
+    public void testPutAndGet()
+    {
+
+
+        final UUID uuid = UUIDGenerator.newTimeUUID();
+        final UUID version = UUIDGenerator.newTimeUUID();
+        final String type = "test";
+        final long created = 1l;
+        final long updated = 2l;
+
+        Entity entity = new Entity( uuid, type );
+
+        entity.setVersion( version );
+        entity.setCreated( created );
+        entity.setUpdated( updated );
+
+        assertEquals( uuid, entity.getUuid() );
+        assertEquals( type, entity.getType() );
+        assertEquals( created, entity.getCreated() );
+        assertEquals( updated, entity.getUpdated() );
+
+
+        BooleanField boolField = new BooleanField( "boolean", false );
+        DoubleField doubleField = new DoubleField( "double", 1d );
+        IntegerField intField = new IntegerField( "long", 1 );
+        LongField longField = new LongField( "int", 1l );
+        StringField stringField = new StringField( "name", "test" );
+        UUIDField uuidField = new UUIDField( "uuid", UUIDGenerator.newTimeUUID() );
+
+        entity.setField( boolField );
+        entity.setField( doubleField );
+        entity.setField( intField );
+        entity.setField( longField );
+        entity.setField( stringField );
+        entity.setField( uuidField );
+
+        Field<Boolean> boolFieldReturned = entity.getField( boolField.getName() );
+
+        assertSame( boolField, boolFieldReturned );
+
+        Field<Double> doubleFieldReturned = entity.getField( doubleField.getName() );
+
+        assertSame( doubleField, doubleFieldReturned );
+
+        Field<Integer> intFieldReturned = entity.getField( intField.getName() );
+
+        assertSame( intField, intFieldReturned );
+
+        Field<Long> longFieldReturned = entity.getField( longField.getName() );
+
+        assertSame( longField, longFieldReturned );
+
+        Field<String> stringFieldReturned = entity.getField( stringField.getName() );
+
+        assertSame( stringField, stringFieldReturned );
+
+        Field<UUID> uuidFieldReturned = entity.getField( uuidField.getName() );
+
+        assertSame( uuidField, uuidFieldReturned );
+
+
+        Set<Field> results = new HashSet<Field>();
+        results.addAll( entity.getFields() );
+
+
+        assertTrue( results.contains( boolField ) );
+        assertTrue( results.contains( doubleField ) );
+        assertTrue( results.contains( intField ) );
+        assertTrue( results.contains( longField ) );
+        assertTrue( results.contains( stringField ) );
+        assertTrue( results.contains( uuidField ) );
+
+        assertEquals( 6, results.size() );
+
+
+        assertEquals( uuid, entity.getUuid() );
+        assertEquals( version, entity.getVersion() );
+    }
+
+
+    @Test( expected = NullPointerException.class )
+    public void uuidRequired()
+    {
+        new Entity( null, "test" );
+    }
+
+
+    @Test( expected = NullPointerException.class )
+    public void versionRequired()
+    {
+        final UUID uuid = UUIDGenerator.newTimeUUID();
+
+        new Entity( uuid, null );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/Readme.md
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/Readme.md b/stack/corepersistence/perftest/Readme.md
new file mode 100644
index 0000000..8061dda
--- /dev/null
+++ b/stack/corepersistence/perftest/Readme.md
@@ -0,0 +1,52 @@
+# A Simple Performance Testing Framework on AWS
+
+This is a simple performance testing framework designed to pound the heck out 
+of a clustered persistence teir. It is designed as a web application that 
+can be run on several tomcat or jetty servers to bombard in unison an in JVM 
+API that operates against a clustered data storage layer like Cassandra.
+
+## Setting up a Perftest
+
+The framework simply executes a number of calls which you specify using a 
+Perftest implementation class. This class specifies all the parameters as 
+methods and is construct by the framework using a TestModule (a guice module)
+which you also provide.
+
+The framework simply loads your TestModule and uses its Guice Injector to 
+create the Perftest instance. It coordinates executing calls against the
+Perftest instance across the set of servers containing the framework. SimpleDB
+is used to communicate presence to all the nodes of the workers so they can
+find each other and coordinate their calls at the same time against the 
+Perftest instance.
+
+The following endpoints are used to control the framework:
+
+ * /perftest/start
+ * /perftest/stop
+ * /perftest/reset
+ * /perftest/stats
+
+The following ascii text shows the states of the framework which one can 
+go through while issuing POSTs to the end points above:
+
+            start           stop
+    +-----+       +-------+     +-------+
+ ---+ready+-------+running+-----+stopped|
+    +--+--+       +-------+     +---+---+
+       |                            |
+       |____________________________|
+                   reset
+
+A post to a single node issues the same POST to all the nodes in the cluster.
+
+## Dependencies
+
+It uses the following libraries to do what it does:
+
+* Jersey - for REST services
+* Jackson - for JSON <--> Java marshalling
+* Guice - for the DI container
+* Archaius - for dynamic properties
+* Blitz4j - for asynchronous logging
+* Slf4j - API for binding to Blitz4j
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/perftest/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/perftest/pom.xml b/stack/corepersistence/perftest/pom.xml
new file mode 100644
index 0000000..b5c70ee
--- /dev/null
+++ b/stack/corepersistence/perftest/pom.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>persistence</artifactId>
+    <groupId>org.apache.usergrid</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>perftest</artifactId>
+  <packaging>war</packaging>
+  <description>Simple Performance Testing Framework</description>
+
+  <properties>
+    <slf4j.version>1.6.1</slf4j.version>
+    <blitz4j.version>1.31</blitz4j.version>
+    <archaius.version>0.4.1</archaius.version>
+    <servo.version>0.4.36</servo.version>
+    <jersey.version>1.9.1</jersey.version>
+    <jackson.version>2.1.5</jackson.version>
+    <fastxml.version>2.3.0-SNAPSHOT</fastxml.version>
+    <jetty.plugin.version>8.1.14.v20131031</jetty.plugin.version>
+    <jetty.version>9.1.0.M0</jetty.version>
+    <log4j.version>1.2.17</log4j.version>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <guice.version>3.0</guice.version>
+  </properties>
+
+  <build>
+    <finalName>${pom.artifactId}</finalName>
+
+    <plugins>
+      <plugin>
+        <groupId>org.mortbay.jetty</groupId>
+        <artifactId>jetty-maven-plugin</artifactId>
+        <version>${jetty.plugin.version}</version>
+        <configuration>
+          <scanIntervalSeconds>5</scanIntervalSeconds>
+          <webApp>
+            <contextPath>/</contextPath>
+          </webApp>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.sun.jersey</groupId>
+      <artifactId>jersey-server</artifactId>
+      <version>${jersey.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.sun.jersey.contribs</groupId>
+      <artifactId>jersey-guice</artifactId>
+      <version>${jersey.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.fasterxml.jackson.jaxrs</groupId>
+      <artifactId>jackson-jaxrs-json-provider</artifactId>
+      <version>${jackson.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.fasterxml.jackson.module</groupId>
+      <artifactId>jackson-module-guice</artifactId>
+      <version>${fastxml.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.google.inject</groupId>
+      <artifactId>guice</artifactId>
+      <version>${guice.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.google.inject.extensions</groupId>
+      <artifactId>guice-multibindings</artifactId>
+      <version>${guice.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.google.inject.extensions</groupId>
+      <artifactId>guice-servlet</artifactId>
+      <version>${guice.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlet</artifactId>
+      <version>${jetty.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-server</artifactId>
+      <version>${jetty.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.netflix.archaius</groupId>
+      <artifactId>archaius-core</artifactId>
+      <version>${archaius.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.netflix.blitz4j</groupId>
+      <artifactId>blitz4j</artifactId>
+      <version>${blitz4j.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.netflix.archaius</groupId>
+      <artifactId>archaius-aws</artifactId>
+      <version>${archaius.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${slf4j.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>${slf4j.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>log4j</groupId>
+          <artifactId>log4j</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+
+    <!-- Test RelatedDependencies -->
+
+    <dependency>
+      <groupId>com.google.guiceberry</groupId>
+      <artifactId>guiceberry</artifactId>
+      <version>3.3.1</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
+


[3/3] git commit: Initial import of 2.0 core persistence code.

Posted by to...@apache.org.
Initial import of 2.0 core persistence code.


Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/ac634a1d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/ac634a1d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/ac634a1d

Branch: refs/heads/two-dot-o
Commit: ac634a1d05e3f8eedc99eb06f5d7b97c3b437d23
Parents: a30bf50
Author: Todd Nine <tn...@apigee.com>
Authored: Wed Nov 27 15:08:46 2013 -0700
Committer: Todd Nine <tn...@apigee.com>
Committed: Wed Nov 27 15:08:46 2013 -0700

----------------------------------------------------------------------
 stack/corepersistence/.gitignore                |   7 +
 stack/corepersistence/README.md                 |  73 ++++
 stack/corepersistence/collection/pom.xml        | 113 ++++++
 .../collection/CollecitonManagerImpl.java       |  15 +
 .../collection/CollectionContext.java           |  25 ++
 .../collection/CollectionContextImpl.java       |  82 +++++
 .../collection/CollectionManager.java           |  41 +++
 .../collection/CollectionManagerFactory.java    |  18 +
 .../CollectionManagerFactoryImpl.java           |  15 +
 .../collection/CollectionManagerImpl.java       |  45 +++
 .../astynax/AstynaxKeyspaceProvider.java        |  99 ++++++
 .../collection/guice/CollectionModule.java      |  64 ++++
 .../collection/guice/PropertyUtils.java         |  60 ++++
 .../migration/CollectionColumnFamily.java       |  50 +++
 .../collection/migration/Migration.java         |  16 +
 .../migration/MigrationException.java           |  18 +
 .../collection/migration/MigrationManager.java  |  15 +
 .../migration/MigrationManagerImpl.java         | 198 +++++++++++
 .../collection/mvcc/entity/MvccEntity.java      |  38 ++
 .../collection/mvcc/entity/MvccEntityImpl.java  |  95 +++++
 .../collection/mvcc/entity/MvccLogEntry.java    |  38 ++
 .../mvcc/entity/MvccLogEntryImpl.java           |  90 +++++
 .../collection/mvcc/entity/Stage.java           |  67 ++++
 .../mvcc/event/PostProcessListener.java         |  24 ++
 .../collection/mvcc/stage/Commit.java           |  18 +
 .../collection/mvcc/stage/MvccStrategy.java     |  42 +++
 .../collection/mvcc/stage/Start.java            |  24 ++
 .../collection/mvcc/stage/Write.java            |  25 ++
 .../collection/mvcc/stage/WriteStage.java       |  20 ++
 .../collection/mvcc/verify/AtomicUpdate.java    |  26 ++
 .../mvcc/verify/OptimisticUpdate.java           |  22 ++
 .../collection/mvcc/verify/UniqueUpdate.java    |  27 ++
 .../MvccEntitySerializationStrategy.java        |  77 +++++
 .../MvccEntitySerializationStrategyImpl.java    | 252 ++++++++++++++
 .../MvccLogEntrySerializationStrategy.java      |  59 ++++
 .../MvccLogEntrySerializationStrategyImpl.java  | 219 ++++++++++++
 .../CollectionManagerFactoryTest.java           |   8 +
 .../collection/guice/TestCollectionModule.java  | 103 ++++++
 .../collection/mvcc/entity/StageTest.java       |  91 +++++
 ...MvccEntitySerializationStrategyImplTest.java | 343 +++++++++++++++++++
 ...ccLogEntrySerializationStrategyImplTest.java | 150 ++++++++
 .../serialization/SerializationComparison.java  | 182 ++++++++++
 .../src/test/resources/cassandra.properties     |   5 +
 stack/corepersistence/index/pom.xml             |  22 ++
 .../usergrid/persistence/index/Query.java       |   6 +
 .../usergrid/persistence/index/QueryEngine.java |  22 ++
 .../persistence/index/QueryEngineFactory.java   |  21 ++
 .../usergrid/persistence/index/Results.java     |  16 +
 .../persistence/index/stage/Complete.java       |  21 ++
 .../usergrid/persistence/index/stage/Start.java |  16 +
 .../usergrid/persistence/index/stage/Write.java |  17 +
 stack/corepersistence/model/pom.xml             |  38 ++
 .../persistence/model/entity/Entity.java        | 149 ++++++++
 .../persistence/model/field/AbstractField.java  |  85 +++++
 .../persistence/model/field/ArrayField.java     |  27 ++
 .../persistence/model/field/BooleanField.java   |  19 +
 .../model/field/ByteBufferField.java            |  29 ++
 .../persistence/model/field/DoubleField.java    |  20 ++
 .../model/field/EntityObjectField.java          |  20 ++
 .../usergrid/persistence/model/field/Field.java |  28 ++
 .../persistence/model/field/IntegerField.java   |  20 ++
 .../persistence/model/field/ListField.java      |  28 ++
 .../persistence/model/field/LocationField.java  |  20 ++
 .../persistence/model/field/LongField.java      |  23 ++
 .../persistence/model/field/SetField.java       |  28 ++
 .../persistence/model/field/StringField.java    |  17 +
 .../persistence/model/field/UUIDField.java      |  20 ++
 .../persistence/model/util/UUIDGenerator.java   | 106 ++++++
 .../persistence/model/value/EntityObject.java   |  40 +++
 .../persistence/model/value/Location.java       |  32 ++
 .../persistence/model/field/EntityTest.java     | 117 +++++++
 stack/corepersistence/perftest/Readme.md        |  52 +++
 stack/corepersistence/perftest/pom.xml          | 150 ++++++++
 .../apache/usergrid/perftest/NoopPerftest.java  |  54 +++
 .../usergrid/perftest/NoopPerftestModule.java   |  34 ++
 .../org/apache/usergrid/perftest/Perftest.java  |  11 +
 .../usergrid/perftest/PerftestModule.java       |  47 +++
 .../usergrid/perftest/PerftestRunner.java       | 197 +++++++++++
 .../perftest/PerftestServletConfig.java         |  50 +++
 .../usergrid/perftest/TestModuleLoader.java     |  95 +++++
 .../apache/usergrid/perftest/logging/Log.java   |  19 +
 .../perftest/logging/Slf4jMembersInjector.java  |  46 +++
 .../perftest/logging/Slf4jTypeListener.java     |  44 +++
 .../perftest/rest/CallStatsSnapshot.java        |  91 +++++
 .../perftest/rest/PerftestResetResource.java    |  64 ++++
 .../perftest/rest/PerftestStartResource.java    |  64 ++++
 .../perftest/rest/PerftestStatsResource.java    |  53 +++
 .../perftest/rest/PerftestStatusResource.java   |  62 ++++
 .../perftest/rest/PerftestStopResource.java     |  59 ++++
 .../usergrid/perfteststats/CallStats.java       |  90 +++++
 .../src/main/resources/config.properties        |  22 ++
 .../src/main/resources/log4j.properties         |  20 ++
 .../perftest/src/main/webapp/WEB-INF/web.xml    |  27 ++
 stack/corepersistence/pom.xml                   |  30 ++
 stack/corepersistence/testutils/pom.xml         |  43 +++
 .../persistence/test/AvailablePortFinder.java   | 187 ++++++++++
 .../persistence/test/CassandraRule.java         |  86 +++++
 .../src/main/resources/log4j-server.properties  |  35 ++
 .../testutils/src/main/resources/log4j.xml      |  16 +
 99 files changed, 5754 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/.gitignore
----------------------------------------------------------------------
diff --git a/stack/corepersistence/.gitignore b/stack/corepersistence/.gitignore
new file mode 100644
index 0000000..7e23415
--- /dev/null
+++ b/stack/corepersistence/.gitignore
@@ -0,0 +1,7 @@
+.idea
+atlassian-ide-plugin.xml
+target
+**/target
+*.iml
+*.swp
+*.log

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/README.md
----------------------------------------------------------------------
diff --git a/stack/corepersistence/README.md b/stack/corepersistence/README.md
new file mode 100644
index 0000000..5f9a44b
--- /dev/null
+++ b/stack/corepersistence/README.md
@@ -0,0 +1,73 @@
+Core Persistence
+===============
+
+A Framework to provide basic component services for persistence frameworks
+
+
+Data Templates
+==============
+
+Below are the basic data templates this system should support
+
+
+Collections
+-----------
+
+A collection storage and indexing framework.  Properties should be secondary indexed, and should be able to be queried efficiently.
+
+
+*MVCC Semantics*
+
+Transaction/Checkpoint logging on indexing.
+Consistent data view.  Can potentially be for long running jobs.
+Optimistic Locking (maybe)
+Atomic updates (maybe)
+
+*Operation Chaining* (maybe)
+
+Possible ability to define an operation context where a set of all writes must either succeed or fail as a group
+(can probably be done with MVCC)
+
+
+
+
+Graphs
+-----------
+
+A system for creating relationships between collection entities.  The directed edges can be named (a type) and
+an index query can be executed on those edges.
+
+
+
+Maps
+-----------
+
+A map that can store hierarchical keys.  Shorter keys are better.  This should allow for range "scanning".  I.E.
+
+key1: => org1/app1/env1/version1
+
+key2: => org1/app1/env2/version1
+
+Operations:
+
+ Put by key
+ Get by key
+ Iterate by scan
+ Delete by key
+
+
+Get me all keys present in org1/app1.
+
+Start => org1/app1
+
+End => org1/app1 inclusive
+
+-----------
+===========
+
+A write through distributed cache backed by the cassandra map implementation for persistence
+
+
+
+
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/pom.xml b/stack/corepersistence/collection/pom.xml
new file mode 100644
index 0000000..47875bc
--- /dev/null
+++ b/stack/corepersistence/collection/pom.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <artifactId>persistence</artifactId>
+    <groupId>org.apache.usergrid</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <description>The module for handling all collection I/O</description>
+  <properties>
+    <guice.version>3.0</guice.version>
+  </properties>
+
+  <artifactId>collection</artifactId>
+
+  <dependencies>
+
+    <!-- Depends on the basic models -->
+    <dependency>
+      <groupId>${project.parent.groupId}</groupId>
+      <artifactId>model</artifactId>
+      <version>${project.parent.version}</version>
+    </dependency>
+
+    <!-- include our cassandra client -->
+    <dependency>
+      <groupId>com.netflix.astyanax</groupId>
+      <artifactId>astyanax-core</artifactId>
+      <version>${astynax.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.netflix.astyanax</groupId>
+      <artifactId>astyanax-thrift</artifactId>
+      <version>${astynax.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>com.netflix.astyanax</groupId>
+      <artifactId>astyanax-cassandra</artifactId>
+      <version>${astynax.version}</version>
+    </dependency>
+
+    <!-- Serialization libraries -->
+
+    <dependency>
+        <groupId>org.codehaus.jackson</groupId>
+        <artifactId>jackson-smile</artifactId>
+        <version>1.9.13</version>
+    </dependency>
+
+    <dependency>
+        <groupId>com.esotericsoftware.kryo</groupId>
+        <artifactId>kryo</artifactId>
+        <version>2.22</version>
+    </dependency>
+
+    <!-- helper serializers for kryo -->
+    <dependency>
+        <groupId>de.javakaffee</groupId>
+        <artifactId>kryo-serializers</artifactId>
+        <version>0.26</version>
+    </dependency>
+
+    <!-- Google Guice -->
+    <dependency>
+      <groupId>com.google.inject</groupId>
+      <artifactId>guice</artifactId>
+      <version>${guice.version}</version>
+    </dependency>
+
+    <!-- guice helper removed for now, investigate using this later, it's getting in the way right now while learning guice -->
+    <!--<dependency>
+      <groupId>com.netflix.governator</groupId>
+      <artifactId>governator</artifactId>
+      <version>1.2.3</version>
+    </dependency> -->
+
+    <dependency>
+      <groupId>com.google.inject.extensions</groupId>
+      <artifactId>guice-multibindings</artifactId>
+      <version>${guice.version}</version>
+    </dependency>
+
+    <!-- Google Guice Integration Test Injectors -->
+
+    <dependency>
+      <groupId>com.google.guiceberry</groupId>
+      <artifactId>guiceberry</artifactId>
+      <version>3.3.1</version>
+      <scope>test</scope>
+    </dependency>
+
+
+    <dependency>
+      <groupId>${project.parent.groupId}</groupId>
+      <artifactId>testutils</artifactId>
+      <version>${project.parent.version}</version>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+    <groupId>org.mockito</groupId>
+     <artifactId>mockito-all</artifactId>
+     <version>1.9.5</version>
+     <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollecitonManagerImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollecitonManagerImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollecitonManagerImpl.java
new file mode 100644
index 0000000..5a2f708
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollecitonManagerImpl.java
@@ -0,0 +1,15 @@
+package org.apache.usergrid.persistence.collection;
+
+
+/**
+ * @author tnine
+ */
+public class CollecitonManagerImpl {
+
+    private final CollectionContext context;
+
+
+    public CollecitonManagerImpl( final CollectionContext context ) {
+        this.context = context;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContext.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContext.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContext.java
new file mode 100644
index 0000000..dd30d3a
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContext.java
@@ -0,0 +1,25 @@
+package org.apache.usergrid.persistence.collection;
+
+
+import java.util.UUID;
+
+
+/**
+ * A context to use when creating the collection manager.  Typically, this would be something like an application, or an
+ * organization.  Some context that "owns" the collection
+ */
+public interface CollectionContext
+{
+
+    /** @return The application that will contain this collection */
+    public UUID getApplication();
+
+    /**
+     * @return A uuid that is unique to this context.  It can be any uuid (time uuid preferred).  Usually an application
+     *         Id, but could be an entity Id that is the parent of another collection
+     */
+    public UUID getOwner();
+
+    /** @return The name of the collection. This should be singular, NO PLURALIZATION!!!!!! */
+    public String getName();
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContextImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContextImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContextImpl.java
new file mode 100644
index 0000000..371bde9
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionContextImpl.java
@@ -0,0 +1,82 @@
+package org.apache.usergrid.persistence.collection;
+
+
+import java.util.UUID;
+
+import com.google.common.base.Preconditions;
+
+
+/**
+ * Simple impl of hte collection context
+ * @author tnine
+ */
+public class CollectionContextImpl implements CollectionContext {
+
+    private final UUID applicationId;
+    private final UUID ownerId;
+    private final String name;
+
+
+    public CollectionContextImpl( final UUID applicationId, final UUID ownerId, final String name ) {
+        Preconditions.checkNotNull( applicationId , "applicationId is required");
+        Preconditions.checkNotNull( ownerId , "ownerId is required");
+        Preconditions.checkNotNull( name , "name is required");
+
+
+        this.applicationId = applicationId;
+        this.ownerId = ownerId;
+        this.name = name;
+    }
+
+
+    @Override
+    public UUID getApplication() {
+        return applicationId;
+    }
+
+
+    @Override
+    public UUID getOwner() {
+        return ownerId;
+    }
+
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+
+    @Override
+    public boolean equals( final Object o ) {
+        if ( this == o ) {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() ) {
+            return false;
+        }
+
+        final CollectionContextImpl that = ( CollectionContextImpl ) o;
+
+        if ( !applicationId.equals( that.applicationId ) ) {
+            return false;
+        }
+        if ( !name.equals( that.name ) ) {
+            return false;
+        }
+        if ( !ownerId.equals( that.ownerId ) ) {
+            return false;
+        }
+
+        return true;
+    }
+
+
+    @Override
+    public int hashCode() {
+        int result = applicationId.hashCode();
+        result = 31 * result + ownerId.hashCode();
+        result = 31 * result + name.hashCode();
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManager.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManager.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManager.java
new file mode 100644
index 0000000..570a1e4
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManager.java
@@ -0,0 +1,41 @@
+package org.apache.usergrid.persistence.collection;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+
+/**
+ *
+ *
+ * @author: tnine
+ *
+ */
+public interface CollectionManager
+{
+
+    /**
+     * Create the entity in the collection.  Only use for entities your are sure are new.
+     *
+     * @param entity The entity to update
+     */
+    public void create( Entity entity );
+
+    /**
+     * Update the entity with the given fields.
+     *
+     * @param entity The entity properties to update
+     */
+    public void update( Entity entity );
+
+    /** Delete the entity and remove it's indexes with the given entity id */
+    public void delete( UUID entityId );
+
+    /**
+     * Load the entity with the given entity Id
+     * @param entityId
+     * @return
+     */
+    public Entity load(UUID entityId);
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactory.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactory.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactory.java
new file mode 100644
index 0000000..7791a0b
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactory.java
@@ -0,0 +1,18 @@
+package org.apache.usergrid.persistence.collection;
+
+
+/** A basic factory that creates a collection manager with the given context */
+public interface CollectionManagerFactory
+{
+
+    /**
+     * Create a new CollectionManager for the given context. The CollectionManager can safely be used on the current
+     * thread and will cache responses.  The returned instance should not be shared among threads it will not be
+     * guaranteed to be thread safe
+     *
+     * @param context The context to use when creating the collection manager
+     *
+     * @return The collection manager to perform operations within the provided context
+     */
+    public CollectionManager createCollectionManager( CollectionContext context );
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryImpl.java
new file mode 100644
index 0000000..a6631e6
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerFactoryImpl.java
@@ -0,0 +1,15 @@
+package org.apache.usergrid.persistence.collection;
+
+
+/**
+ * Basic Imple
+ * @author tnine
+ */
+public class CollectionManagerFactoryImpl implements CollectionManagerFactory {
+
+
+    @Override
+    public CollectionManager createCollectionManager( final CollectionContext context ) {
+        return new CollectionManagerImpl( context );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerImpl.java
new file mode 100644
index 0000000..15ef3ff
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/CollectionManagerImpl.java
@@ -0,0 +1,45 @@
+package org.apache.usergrid.persistence.collection;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+
+/**
+ * Simple implementation.  Should perform
+ * @author tnine
+ */
+public class CollectionManagerImpl implements CollectionManager {
+
+    private final CollectionContext context;
+
+
+    public CollectionManagerImpl( final CollectionContext context ) {
+        this.context = context;
+    }
+
+
+    @Override
+    public void create( final Entity entity ) {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+
+    @Override
+    public void update( final Entity entity ) {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+
+    @Override
+    public void delete( final UUID entityId ) {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+
+    @Override
+    public Entity load( final UUID entityId ) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/astynax/AstynaxKeyspaceProvider.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/astynax/AstynaxKeyspaceProvider.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/astynax/AstynaxKeyspaceProvider.java
new file mode 100644
index 0000000..150f221
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/astynax/AstynaxKeyspaceProvider.java
@@ -0,0 +1,99 @@
+package org.apache.usergrid.persistence.collection.astynax;
+
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.name.Named;
+import com.netflix.astyanax.AstyanaxConfiguration;
+import com.netflix.astyanax.AstyanaxContext;
+import com.netflix.astyanax.Keyspace;
+import com.netflix.astyanax.connectionpool.ConnectionPoolConfiguration;
+import com.netflix.astyanax.connectionpool.NodeDiscoveryType;
+import com.netflix.astyanax.connectionpool.impl.ConnectionPoolConfigurationImpl;
+import com.netflix.astyanax.connectionpool.impl.Slf4jConnectionPoolMonitorImpl;
+import com.netflix.astyanax.impl.AstyanaxConfigurationImpl;
+import com.netflix.astyanax.thrift.ThriftFamilyFactory;
+
+
+/**
+ * TODO.  Provide the ability to do a service hook for realtime tuning without the need of a JVM restart
+ * This could be done with governator and service discovery
+ * @author tnine
+ */
+public class AstynaxKeyspaceProvider implements Provider<Keyspace> {
+
+    /**
+     * The cassandra URL property
+     */
+    public static final String CASSANDRA_HOSTS = "cassandra.hosts";
+    public static final String CASSANDRA_PORT = "cassandra.port";
+    public static final String CASSANDRA_CONNECTIONS = "cassandra.connections";
+    public static final String CASSANDRA_CLUSTER_NAME = "cassandra.cluster_name";
+    public static final String CASSANDRA_VERSION = "cassandra.version";
+    public static final String COLLECTIONS_KEYSPACE_NAME = "collections.keyspace";
+
+    protected final String cassandraHosts;
+    protected final int cassandraPort;
+    protected final int cassandraConnections;
+    protected final String clusterName;
+    protected final String keyspaceName;
+    protected final String cassandraVersion;
+
+
+    @Inject
+    public AstynaxKeyspaceProvider( @Named( CASSANDRA_HOSTS ) final String cassandraHosts,
+                                    @Named( CASSANDRA_PORT ) final int cassandraPort,
+                                    @Named( CASSANDRA_CONNECTIONS ) final int cassandraConnections,
+                                    @Named( CASSANDRA_CLUSTER_NAME ) final String clusterName,
+                                    @Named( CASSANDRA_VERSION ) final String cassandraVersion,
+                                    @Named( COLLECTIONS_KEYSPACE_NAME ) final String keyspaceName ) {
+        this.cassandraHosts = cassandraHosts;
+        this.cassandraPort = cassandraPort;
+        this.cassandraConnections = cassandraConnections;
+        this.clusterName = clusterName;
+        this.keyspaceName = keyspaceName;
+        this.cassandraVersion = cassandraVersion;
+    }
+
+
+    @Override
+    public Keyspace get() {
+        AstyanaxConfiguration config = new AstyanaxConfigurationImpl().setDiscoveryType( NodeDiscoveryType.TOKEN_AWARE )
+                                                                      .setTargetCassandraVersion( cassandraVersion );
+
+        ConnectionPoolConfiguration connectionPoolConfiguration =
+                new ConnectionPoolConfigurationImpl( "UsergridConnectionPool" ).setPort( cassandraPort )
+                                                                               .setMaxConnsPerHost(
+                                                                                       cassandraConnections )
+                                                                               .setSeeds( cassandraHosts );
+
+        AstyanaxContext<Keyspace> context =
+                new AstyanaxContext.Builder().forCluster( clusterName ).forKeyspace( keyspaceName )
+                        /**
+                         *TODO tnine Filter this by adding a host supplier.  We will get token discovery from cassandra
+                         * but only connect
+                         * to nodes that have been specified.  Good for real time updates of the cass system without adding
+                         * load to them during runtime
+                         */.withAstyanaxConfiguration( config )
+                           .withConnectionPoolConfiguration( connectionPoolConfiguration )
+                           .withConnectionPoolMonitor( new Slf4jConnectionPoolMonitorImpl() )
+                           .buildKeyspace( ThriftFamilyFactory.getInstance() );
+
+        context.start();
+
+
+        return context.getClient();
+    }
+
+
+    /**
+     * Get runtime options that can be overridden.  TODO: Make this an interface and somehow hook it into Guice auotmagically
+     * @return
+     */
+    public static String[] getRuntimeOptions() {
+        return new String[] {
+                CASSANDRA_HOSTS, CASSANDRA_PORT, CASSANDRA_CONNECTIONS, CASSANDRA_CLUSTER_NAME, CASSANDRA_VERSION,
+                COLLECTIONS_KEYSPACE_NAME
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/CollectionModule.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/CollectionModule.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/CollectionModule.java
new file mode 100644
index 0000000..c83fe4f
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/CollectionModule.java
@@ -0,0 +1,64 @@
+package org.apache.usergrid.persistence.collection.guice;
+
+
+import org.apache.usergrid.persistence.collection.astynax.AstynaxKeyspaceProvider;
+import org.apache.usergrid.persistence.collection.migration.Migration;
+import org.apache.usergrid.persistence.collection.migration.MigrationManager;
+import org.apache.usergrid.persistence.collection.migration.MigrationManagerImpl;
+import org.apache.usergrid.persistence.collection.serialization.MvccEntitySerializationStrategy;
+import org.apache.usergrid.persistence.collection.serialization.MvccEntitySerializationStrategyImpl;
+import org.apache.usergrid.persistence.collection.serialization.MvccLogEntrySerializationStrategy;
+import org.apache.usergrid.persistence.collection.serialization.MvccLogEntrySerializationStrategyImpl;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Names;
+import com.netflix.astyanax.Keyspace;
+
+
+/**
+ * Simple module for wiring our collection api
+ *
+ * @author tnine
+ */
+public class CollectionModule extends AbstractModule {
+
+    /**
+     * The location of the properties file
+     */
+    private static final String CASS_PROPS = "cassandra.properties";
+
+
+    @Override
+    protected void configure() {
+
+
+        //bind our cassandra properties
+        Names.bindProperties( binder(), PropertyUtils.loadFromClassPath( CASS_PROPS ) );
+
+        //Load the cassandra url if set on the system properties
+        Names.bindProperties( binder(),
+                PropertyUtils.loadSystemProperties( AstynaxKeyspaceProvider.getRuntimeOptions() ) );
+
+        //bind our keyspace to the AstynaxKeyspaceProvider
+        bind( Keyspace.class ).toProvider( AstynaxKeyspaceProvider.class );
+
+        //bind our migration manager
+        bind(MigrationManager.class).to( MigrationManagerImpl.class );
+
+
+        //bind the serialization strategies
+
+        bind( MvccEntitySerializationStrategy.class ).to( MvccEntitySerializationStrategyImpl.class );
+
+
+        bind( MvccLogEntrySerializationStrategy.class ).to( MvccLogEntrySerializationStrategyImpl.class );
+
+
+        //do multibindings for migrations
+        Multibinder<Migration> uriBinder = Multibinder.newSetBinder( binder(), Migration.class );
+
+        uriBinder.addBinding().to( MvccEntitySerializationStrategyImpl.class );
+        uriBinder.addBinding().to( MvccLogEntrySerializationStrategyImpl.class );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/PropertyUtils.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/PropertyUtils.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/PropertyUtils.java
new file mode 100644
index 0000000..a427135
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/guice/PropertyUtils.java
@@ -0,0 +1,60 @@
+package org.apache.usergrid.persistence.collection.guice;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+
+/**
+ * Simple Utility class to get properties
+ *
+ * @author tnine
+ */
+public class PropertyUtils {
+
+
+    /**
+     * Load the properties file from the classpath.  Throws IOException if they cannot be loaded
+     */
+    public static Properties loadFromClassPath( String propsFile ) {
+        InputStream in = PropertyUtils.class.getClassLoader().getResourceAsStream( propsFile );
+
+        if ( in == null ) {
+            throw new RuntimeException( new IOException(
+                    String.format( "Could not find properties file on the classpath at location %s", propsFile ) ) );
+        }
+
+        Properties props = new Properties();
+
+        try {
+            props.load( in );
+        }
+        catch ( IOException e ) {
+            throw new RuntimeException( e );
+        }
+
+        return props;
+    }
+
+
+    /**
+     * Load each of the defined properties into a system property and return them.  If a system property is not found,
+     * it will be ignored
+     */
+    public static Properties loadSystemProperties( String... properties ) {
+
+        Properties props = new Properties();
+
+        for ( String propName : properties ) {
+            String propValue = System.getProperty( propName );
+
+            if ( propValue != null ) {
+                props.put( propName, propValue );
+            }
+        }
+
+
+        return props;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/CollectionColumnFamily.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/CollectionColumnFamily.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/CollectionColumnFamily.java
new file mode 100644
index 0000000..a8036bd
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/CollectionColumnFamily.java
@@ -0,0 +1,50 @@
+package org.apache.usergrid.persistence.collection.migration;
+
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.netflix.astyanax.model.ColumnFamily;
+
+
+/**
+ * Bean wrapper for column family information
+ *
+ * @author tnine
+ */
+public class CollectionColumnFamily {
+
+    public static final String COMPARATOR_TYPE = "comparator_type";
+    public static final String REVERSED = "reversed";
+    public static final String READ_REPAIR_CHANCE = "read_repair_chance";
+
+
+    private final ColumnFamily columnFamily;
+    private final String comparator;
+    private final boolean reversed;
+
+
+    public CollectionColumnFamily( final ColumnFamily columnFamily, final String comparator, final boolean reversed ) {
+        this.columnFamily = columnFamily;
+        this.comparator = comparator;
+        this.reversed = reversed;
+    }
+
+
+    public Map<String, Object> getOptions(){
+
+        Map<String, Object> options = new HashMap<String, Object>();
+        options.put( COMPARATOR_TYPE, comparator );
+        options.put( REVERSED, reversed );
+
+        //always use 10% read repair chance!
+        options.put( READ_REPAIR_CHANCE, 0.1d );
+
+        return options;
+    }
+
+
+    public ColumnFamily getColumnFamily() {
+        return columnFamily;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/Migration.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/Migration.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/Migration.java
new file mode 100644
index 0000000..f446031
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/Migration.java
@@ -0,0 +1,16 @@
+package org.apache.usergrid.persistence.collection.migration;
+
+
+import java.util.Collection;
+
+
+/**
+ * @author tnine
+ */
+public interface Migration {
+
+    /**
+     * Get the column families required for this implementation.  If one does not exist it will be created.
+     */
+    public Collection<CollectionColumnFamily> getColumnFamilies();
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationException.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationException.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationException.java
new file mode 100644
index 0000000..5eed4fd
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationException.java
@@ -0,0 +1,18 @@
+package org.apache.usergrid.persistence.collection.migration;
+
+
+/**
+ * Thrown when a migration cannot be performed
+ * @author tnine
+ */
+public class MigrationException extends Exception {
+
+    public MigrationException( final String message ) {
+        super( message );
+    }
+
+
+    public MigrationException( final String message, final Throwable cause ) {
+        super( message, cause );
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManager.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManager.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManager.java
new file mode 100644
index 0000000..d3d7038
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManager.java
@@ -0,0 +1,15 @@
+package org.apache.usergrid.persistence.collection.migration;
+
+
+/**
+ * A manager that will perform any migrations necessary.  Setup code should invoke the implementation of this interface
+ *
+ * @author tnine
+ */
+public interface MigrationManager {
+
+    /**
+     * Perform any migration necessary in the application.  Will only create keyspaces and column families if they do not exist
+     */
+    public void migrate() throws MigrationException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManagerImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManagerImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManagerImpl.java
new file mode 100644
index 0000000..a57dcef
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/migration/MigrationManagerImpl.java
@@ -0,0 +1,198 @@
+package org.apache.usergrid.persistence.collection.migration;
+
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+import com.netflix.astyanax.Keyspace;
+import com.netflix.astyanax.connectionpool.exceptions.BadRequestException;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+import com.netflix.astyanax.ddl.ColumnFamilyDefinition;
+import com.netflix.astyanax.ddl.KeyspaceDefinition;
+
+
+/**
+ * Implementation of the migration manager to set up keyspace
+ *
+ * @author tnine
+ */
+@Singleton
+public class MigrationManagerImpl implements MigrationManager {
+
+
+    private static final Logger logger = LoggerFactory.getLogger( MigrationManagerImpl.class );
+
+    public static final String STRATEGY_CLASS = "collections.keyspace.strategy.class";
+    public static final String STRATEGY_OPTIONS = "collections.keyspace.strategy.options";
+    public static final String REPLICATION_FACTOR = "collections.keyspace.replicationfactor";
+
+
+    private final String strategyClass;
+    private final String replicationFactor;
+
+
+    private final Set<Migration> migrations;
+    private final Keyspace keyspace;
+    private final Properties props;
+
+
+    @Inject
+    public MigrationManagerImpl( final Keyspace keyspace, final Set<Migration> migrations, final Properties props,
+                                 @Named( STRATEGY_CLASS ) final String strategyClass,
+                                 @Named( REPLICATION_FACTOR ) final String replicationFactor ) {
+        this.keyspace = keyspace;
+        this.migrations = migrations;
+        this.props = props;
+        this.strategyClass = strategyClass;
+        this.replicationFactor = replicationFactor;
+    }
+
+
+    @Override
+    public void migrate() throws MigrationException {
+
+
+        try {
+
+            testAndCreateKeyspace();
+
+            for ( Migration migration : migrations ) {
+
+                final Collection<CollectionColumnFamily> columnFamilies = migration.getColumnFamilies();
+
+
+                if(columnFamilies == null){
+                    logger.warn( "Class {} implements {} but returns null column families for migration.  Either implement this method or remove the interface from the class", migration.getClass(), Migration.class );
+                    continue;
+                }
+
+                for ( CollectionColumnFamily cf :  columnFamilies) {
+                    testAndCreateColumnFamilyDef( cf );
+                }
+            }
+        }
+        catch ( Throwable t ) {
+            logger.error( "Unable to perform migration", t );
+            throw new MigrationException( "Unable to perform migration", t );
+        }
+    }
+
+
+    /**
+     * Check if the column family exists.  If it dosn't create it
+     */
+    private void testAndCreateColumnFamilyDef( CollectionColumnFamily columnFamily ) throws ConnectionException {
+        final KeyspaceDefinition keyspaceDefinition = keyspace.describeKeyspace();
+
+        final ColumnFamilyDefinition existing =
+                keyspaceDefinition.getColumnFamily( columnFamily.getColumnFamily().getName() );
+
+        if ( existing != null ) {
+            return;
+        }
+
+        keyspace.createColumnFamily( columnFamily.getColumnFamily(), columnFamily.getOptions() );
+
+        waitForMigration();
+    }
+
+
+    /**
+     * Check if they keyspace exists.  If it doesn't create it
+     */
+    private void testAndCreateKeyspace() throws ConnectionException {
+
+
+        KeyspaceDefinition keyspaceDefinition = null;
+
+        try {
+            keyspaceDefinition = keyspace.describeKeyspace();
+        }
+        catch ( BadRequestException badRequestException ) {
+
+            //check if it's b/c the keyspace is missing, if so
+            final String message = badRequestException.getMessage();
+
+            boolean missingKeyspace = message.contains( "why:Keyspace" ) && message.contains( "does not exist" );
+
+            if ( !missingKeyspace ) {
+                throw badRequestException;
+            }
+        }
+
+
+        if ( keyspaceDefinition != null ) {
+            return;
+        }
+
+
+        ImmutableMap.Builder<String, Object> strategyOptions =
+                ImmutableMap.<String, Object>builder().put( "replication_factor", replicationFactor );
+
+        strategyOptions.putAll( getKeySpaceProps() );
+
+
+        ImmutableMap<String, Object> options =
+                ImmutableMap.<String, Object>builder().put( "strategy_class", strategyClass )
+                            .put( "strategy_options", strategyOptions.build() ).build();
+
+
+        keyspace.createKeyspace( options );
+
+        waitForMigration();
+    }
+
+
+    /**
+     * Get keyspace properties
+     */
+    private Map<String, String> getKeySpaceProps() {
+        Map<String, String> keyspaceProps = new HashMap<String, String>();
+
+        for ( Map.Entry<Object, Object> entry : props.entrySet() ) {
+            final String key = entry.getKey().toString();
+
+            if ( !key.startsWith( STRATEGY_OPTIONS ) ) {
+                continue;
+            }
+
+            final String optionKey = key.substring( STRATEGY_OPTIONS.length() + 1 );
+
+            keyspaceProps.put( optionKey, entry.getValue().toString() );
+        }
+
+        return keyspaceProps;
+    }
+
+
+    private void waitForMigration() throws ConnectionException {
+
+        while ( true ) {
+
+            final Map<String, List<String>> versions = keyspace.describeSchemaVersions();
+
+            if ( versions != null && versions.size() == 1 ) {
+                return;
+            }
+
+            //sleep and try it again
+            try {
+                Thread.sleep( 100 );
+            }
+            catch ( InterruptedException e ) {
+                //swallow
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntity.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntity.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntity.java
new file mode 100644
index 0000000..3e544ad
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntity.java
@@ -0,0 +1,38 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+import com.google.common.base.Optional;
+
+
+/**
+ * A Marker interface for an in flight update to allow context information to be passed between states
+ */
+public interface MvccEntity {
+
+
+    /**
+     * Get the entity for this context.
+     * @return This will return absent if no data is present.  Otherwise the entity will be contained within the optional
+     */
+    Optional<Entity> getEntity();
+
+    /**
+     * Return the version of this entityId we are attempting to write used in the current context
+     */
+    UUID getVersion();
+
+    /**
+     * Get the UUID of the entity
+     */
+    UUID getUuid();
+
+    /**
+     * Get the collection context this entity belongs i
+     */
+    CollectionContext getContext();
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntityImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntityImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntityImpl.java
new file mode 100644
index 0000000..5c01cb5
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccEntityImpl.java
@@ -0,0 +1,95 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+
+/**
+ * @author tnine
+ */
+public class MvccEntityImpl implements MvccEntity {
+
+    private final CollectionContext context;
+    private final UUID entityId;
+    private final UUID version;
+    private final Optional<Entity> entity;
+
+
+    public MvccEntityImpl( final CollectionContext context, final UUID entityId, final UUID version,
+                           final Optional<Entity> entity ) {
+        Preconditions.checkNotNull( context, "context is required" );
+        Preconditions.checkNotNull( entityId, "entity id is required" );
+        Preconditions.checkNotNull( version, "version id is required" );
+        Preconditions.checkNotNull( entity, "entity  is required" );
+
+        this.context = context;
+        this.entityId = entityId;
+        this.version = version;
+        this.entity = entity;
+    }
+
+
+    @Override
+    public Optional<Entity> getEntity() {
+        return entity;
+    }
+
+
+    @Override
+    public UUID getVersion() {
+        return version;
+    }
+
+
+    @Override
+    public UUID getUuid() {
+        return entityId;
+    }
+
+
+    @Override
+    public CollectionContext getContext() {
+        return context;
+    }
+
+
+    @Override
+    public boolean equals( final Object o ) {
+        if ( this == o ) {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() ) {
+            return false;
+        }
+
+        final MvccEntityImpl that = ( MvccEntityImpl ) o;
+
+        if ( !context.equals( that.context ) ) {
+            return false;
+        }
+        if ( !getUuid().equals( that.getUuid() ) ) {
+            return false;
+        }
+
+        if ( !getVersion().equals( that.getVersion() ) ) {
+            return false;
+        }
+
+        return true;
+    }
+
+
+    @Override
+    public int hashCode() {
+        int result = context.hashCode();
+        result = 31 * result + getUuid().hashCode();
+        result = 31 * result + getVersion().hashCode();
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntry.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntry.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntry.java
new file mode 100644
index 0000000..40ff498
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntry.java
@@ -0,0 +1,38 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+
+
+/**
+ * A Marker interface for an in flight update to allow context information to be passed between states
+ */
+public interface MvccLogEntry {
+
+
+    /**
+     * Get the stage for the current version
+     */
+    Stage getStage();
+
+    /**
+     * Get the entity to add info to the log
+     * @return
+     */
+    UUID getEntityId();
+
+    /**
+     * Get the version of the entity
+     * @return
+     */
+    UUID getVersion();
+
+    /**
+     * Get the context of the entity
+     * @return
+     */
+    CollectionContext getContext();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntryImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntryImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntryImpl.java
new file mode 100644
index 0000000..f0f803b
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/MvccLogEntryImpl.java
@@ -0,0 +1,90 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+
+
+/**
+ * The simple implementation of a log entry
+ * @author tnine
+ */
+public class MvccLogEntryImpl implements MvccLogEntry {
+
+    private final CollectionContext context;
+    private final UUID entityId;
+    private final UUID version;
+    private final Stage stage;
+
+
+    public MvccLogEntryImpl(final CollectionContext context, final UUID entityId, final UUID version,
+                             final Stage stage ) {
+        this.context = context;
+        this.entityId = entityId;
+        this.version = version;
+        this.stage = stage;
+    }
+
+
+    @Override
+    public Stage getStage() {
+        return stage;
+    }
+
+
+    @Override
+    public UUID getEntityId() {
+        return entityId;
+    }
+
+
+    @Override
+    public UUID getVersion() {
+        return version;
+    }
+
+
+    @Override
+    public CollectionContext getContext() {
+        return context;
+    }
+
+
+    @Override
+    public boolean equals( final Object o ) {
+        if ( this == o ) {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() ) {
+            return false;
+        }
+
+        final MvccLogEntryImpl that = ( MvccLogEntryImpl ) o;
+
+        if ( !context.equals( that.context ) ) {
+            return false;
+        }
+        if ( !entityId.equals( that.entityId ) ) {
+            return false;
+        }
+        if ( stage != that.stage ) {
+            return false;
+        }
+        if ( !version.equals( that.version ) ) {
+            return false;
+        }
+
+        return true;
+    }
+
+
+    @Override
+    public int hashCode() {
+        int result = context.hashCode();
+        result = 31 * result + entityId.hashCode();
+        result = 31 * result + version.hashCode();
+        result = 31 * result + stage.hashCode();
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/Stage.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/Stage.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/Stage.java
new file mode 100644
index 0000000..96ca3a6
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/entity/Stage.java
@@ -0,0 +1,67 @@
+package org.apache.usergrid.persistence.collection.mvcc.entity;
+
+
+/**
+ * The different stages that can exist in the commit log
+ */
+public enum Stage {
+
+    /**
+     * These are bitmasks that represent the state's we've been through
+     *
+     * Active => 0000
+     * RollBack => 1000
+     * COMMITTED => 1100
+     * POSTPROCESSOR => 1110
+     * ACTIVE => 1111
+     */
+
+    /**
+     * The entity has started writing but is not yet committed
+     */
+    ACTIVE(true, (byte)0),
+
+    /**
+     * The entity has started writing but not yet committed.
+     */
+    ROLLBACK(true, (byte)1),
+    /**
+     * We have applied enough writes to be able to recover via writeahead logging.  The system can recover from a
+     * crash without data loss at this point
+     */
+    COMMITTED(false, (byte)2),
+    /**
+     * The entity is going through post processing
+     */
+    POSTPROCESS(false, (byte)6),
+
+    /**
+     * The entity has completed all post processing
+     */
+    COMPLETE(false, (byte)14);
+
+
+    private final boolean transientStage;
+    private final byte id;
+
+
+    private Stage(final boolean transientStage, final byte id){
+        this.transientStage = transientStage;
+        this.id = id;
+    }
+
+
+    /**
+     * Returns true if this stage is transient and should not be retained in the datastore permanently
+     * Stages such as start and write don't need to be retained, but can be used to signal "in flight"
+     * updates
+     */
+    public boolean isTransient() {
+        return transientStage;
+    }
+
+    public byte getId(){
+        return this.id;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/event/PostProcessListener.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/event/PostProcessListener.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/event/PostProcessListener.java
new file mode 100644
index 0000000..47ad997
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/event/PostProcessListener.java
@@ -0,0 +1,24 @@
+package org.apache.usergrid.persistence.collection.mvcc.event;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ *
+ * @author: tnine
+ *
+ */
+public interface PostProcessListener<T extends MvccEntity>
+{
+
+
+    /**
+     * The entity was rejected by the MVCC system and will be removed
+     *
+     * @param mvccEntity The mvcc entity to perform post processing on
+     * @return the MvccEntity to use during this stage
+     */
+    public MvccEntity doPostProcessing(MvccEntity mvccEntity );
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Commit.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Commit.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Commit.java
new file mode 100644
index 0000000..64d3c5d
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Commit.java
@@ -0,0 +1,18 @@
+package org.apache.usergrid.persistence.collection.mvcc.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ * This phase should invoke any finalization, and mark the entity as committed in the data store before returning
+ */
+public class Commit implements WriteStage {
+
+
+
+    @Override
+    public MvccEntity performStage( final MvccEntity entity ) {
+        return entity;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/MvccStrategy.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/MvccStrategy.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/MvccStrategy.java
new file mode 100644
index 0000000..c11f420
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/MvccStrategy.java
@@ -0,0 +1,42 @@
+package org.apache.usergrid.persistence.collection.mvcc.stage;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+
+/**
+ * Interface to define mvcc operations
+ *
+ * TODO: Not sure we need this any more
+ */
+public interface MvccStrategy {
+
+    /**
+     * Start progress through states for this entity
+     *
+     * @param context The context this entity belongs in
+     * @param entityId The entity id to assign to this entity
+     * @param entity The entity values to write
+     */
+    public WriteStage beingWrite( CollectionContext context, UUID entityId, Entity entity );
+
+
+    /**
+     * Get the current stage of the entity in the context at the current version.  Should be used for write verification
+     * on resume
+     *
+     * @param context The context this entity belongs in
+     * @param entityId The entity Id to search for in the context
+     * @param version The version of the entityId to review
+     */
+    public WriteStage getCurrentState( CollectionContext context, UUID entityId, UUID version );
+
+
+    /**
+     * Get the write stage of the entity in the context with a version <= the current version and a stage of Comitted
+     */
+    public WriteStage getCurrentStateOfEntity( CollectionContext context, UUID entityId, UUID maxVersion );
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Start.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Start.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Start.java
new file mode 100644
index 0000000..d7f85ae
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Start.java
@@ -0,0 +1,24 @@
+package org.apache.usergrid.persistence.collection.mvcc.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ * This is the first stage and should be invoked immediately when a write is started.  It should persist the start of a
+ * new write in the data store for a checkpoint and recovery
+ */
+public class Start implements WriteStage {
+
+    /**
+     * Create a new stage with the current context
+     */
+    protected Start( ){
+    }
+
+
+    @Override
+    public MvccEntity performStage( final MvccEntity entity ) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Write.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Write.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Write.java
new file mode 100644
index 0000000..b67ca31
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/Write.java
@@ -0,0 +1,25 @@
+package org.apache.usergrid.persistence.collection.mvcc.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ * This phase should execute the serialization to the data store.
+ */
+public class Write implements WriteStage {
+
+    /**
+     * Create a new stage with the current context
+     */
+    protected Write( ){
+    }
+
+
+    @Override
+    public MvccEntity performStage( final MvccEntity entity) {
+
+
+        return entity;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/WriteStage.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/WriteStage.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/WriteStage.java
new file mode 100644
index 0000000..cda707f
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/WriteStage.java
@@ -0,0 +1,20 @@
+package org.apache.usergrid.persistence.collection.mvcc.stage;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ * The possible stages in our write flow.
+ */
+public interface WriteStage {
+
+    /**
+     * Run this stage.  This will return the MvccEntity that should be returned or passed to the next stage
+     * @param entity The entity to use in this stage
+     *
+     * @return The MvccEntity to use for the next sgage
+     *
+     */
+    public MvccEntity performStage( MvccEntity entity);
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/AtomicUpdate.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/AtomicUpdate.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/AtomicUpdate.java
new file mode 100644
index 0000000..d844f3b
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/AtomicUpdate.java
@@ -0,0 +1,26 @@
+package org.apache.usergrid.persistence.collection.mvcc.verify;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/**
+ * Interface to test if we can perform atomic operations
+ * <p/>
+ * Note This will probably require a new WriteStage that is after start, which is rollback
+ */
+public interface AtomicUpdate
+{
+
+    /** Signal that we are starting update */
+    public void startUpdate( MvccEntity context );
+
+    /**
+     * Try the commit.
+     *
+     * @return true if we can proceed.  False if we cannot
+     */
+    public boolean tryCommit( MvccEntity context );
+}
+
+

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/OptimisticUpdate.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/OptimisticUpdate.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/OptimisticUpdate.java
new file mode 100644
index 0000000..cef476f
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/OptimisticUpdate.java
@@ -0,0 +1,22 @@
+package org.apache.usergrid.persistence.collection.mvcc.verify;
+
+
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+
+/** Interface to define how optimistic updates should be performed */
+public interface OptimisticUpdate
+{
+
+    /**
+     * Verify the entity we're trying to write in our current context has the correct most current version
+     *
+     * @param context The mvcc context
+     * @param optimisticVersion The optimistic version the caller provider as the most up to date
+     *
+     * @return True if the optimisticVersion is the most current >= Comitted stage, false otherwise
+     */
+    public boolean verifyCurrent( MvccEntity context, UUID optimisticVersion );
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/UniqueUpdate.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/UniqueUpdate.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/UniqueUpdate.java
new file mode 100644
index 0000000..2df987f
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/verify/UniqueUpdate.java
@@ -0,0 +1,27 @@
+package org.apache.usergrid.persistence.collection.mvcc.verify;
+
+
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.model.field.Field;
+
+
+/** Interface to define how unique updates should be performed */
+public interface UniqueUpdate
+{
+
+    /**
+     * Verify the entity we're trying to write in our current context has the correct most current version
+     *
+     * @param context The mvcc context
+     * @param uniqueField The field to check for uniqueness
+     *
+     * @return True if the value in the uniqueField is unique in the collection context
+     */
+    public boolean verifyUnique( MvccEntity context, Field<?> uniqueField );
+
+    /**
+     * During the commit phase, ensure this entity is committed as a unique value. This may release locks or overwrite
+     * expiring timeout values since we are at the final commit phase
+     */
+    public void commitUnique( MvccEntity entity, Field<?> uniqueField );
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategy.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategy.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategy.java
new file mode 100644
index 0000000..0d3d112
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategy.java
@@ -0,0 +1,77 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+
+import com.netflix.astyanax.MutationBatch;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+
+
+/** The interface that allows us to serialize an entity to disk */
+public interface MvccEntitySerializationStrategy
+{
+
+    /**
+     * Serialize the entity to the data store with the given collection context
+     *
+     * @param entity The entity to persist
+     * @return The MutationBatch operations for this update
+     */
+    public MutationBatch write( MvccEntity entity );
+
+
+    /**
+     * Load and return the entity with the given id and a version that is <= the version provided
+     *
+     * @param context The context to persist the entity into
+     * @param entityId The entity id to load
+     * @param version The version to load.  This will return the version <= the given version
+     *
+     * @return The deserialized version of the entity.  Null if no version == to version exists.
+     * If the entity version has been cleared, the MvccEntity will be returned, but the optional entity
+     * will not be set
+     */
+    public MvccEntity load( CollectionContext context, UUID entityId, UUID version ) throws ConnectionException;
+
+    /**
+     * Load a list, from highest to lowest of the entity with versions <= version up to maxSize elements
+     *
+     * @param context The context to persist the entity into
+     * @param entityId The entity id to load
+     * @param version The max version to seek from.  I.E a stored version <= this argument
+     * @param maxSize The maximum size to return.  If you receive this size, there may be more versions to load.
+     *
+     * @return A list of entities up to max size ordered from max(UUID)=> min(UUID).  The return value should be null safe
+     * and return an empty list when there are no matches
+     */
+    public List<MvccEntity> load( CollectionContext context, UUID entityId, UUID version, int maxSize );
+
+
+    /**
+     * Clear this version from the persistence store, but keep the version to mark that is has been cleared
+     * This can be used in a mark+sweep system.  The entity with the given version will exist in the context,
+     * but no data will be stored
+     *
+     * @param context
+     * @param entityId
+     * @param version
+     * @return
+     */
+    public MutationBatch clear(CollectionContext context, UUID entityId, UUID version);
+
+
+    /**
+     * Delete the entity from the context with the given entityId and version
+     *
+     * @param context The context that contains the entity
+     * @param entityId The entity id to delete
+     * @param version The version to delete
+     */
+    public MutationBatch delete( CollectionContext context, UUID entityId, UUID version );
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImpl.java
new file mode 100644
index 0000000..4b8840d
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccEntitySerializationStrategyImpl.java
@@ -0,0 +1,252 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.cassandra.db.marshal.ReversedType;
+import org.apache.cassandra.db.marshal.UUIDType;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.migration.CollectionColumnFamily;
+import org.apache.usergrid.persistence.collection.migration.Migration;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntity;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccEntityImpl;
+import org.apache.usergrid.persistence.model.entity.Entity;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.netflix.astyanax.ColumnListMutation;
+import com.netflix.astyanax.Keyspace;
+import com.netflix.astyanax.MutationBatch;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+import com.netflix.astyanax.connectionpool.exceptions.NotFoundException;
+import com.netflix.astyanax.model.Column;
+import com.netflix.astyanax.model.ColumnFamily;
+import com.netflix.astyanax.model.ColumnList;
+import com.netflix.astyanax.model.ColumnSlice;
+import com.netflix.astyanax.serializers.AbstractSerializer;
+import com.netflix.astyanax.serializers.ObjectSerializer;
+import com.netflix.astyanax.serializers.UUIDSerializer;
+
+
+/**
+ * @author tnine
+ */
+@Singleton
+public class MvccEntitySerializationStrategyImpl implements MvccEntitySerializationStrategy, Migration {
+
+
+    private static final EntitySerializer SER = new EntitySerializer();
+
+
+    private static final ColumnFamily<UUID, UUID> CF_ENTITY_DATA =
+            new ColumnFamily<UUID, UUID>( "Entity_Version_Data", UUIDSerializer.get(), UUIDSerializer.get() );
+
+
+    protected final Keyspace keyspace;
+
+
+    @Inject
+    public MvccEntitySerializationStrategyImpl( final Keyspace keyspace ) {
+        this.keyspace = keyspace;
+    }
+
+
+    @Override
+    public MutationBatch write( final MvccEntity entity ) {
+        Preconditions.checkNotNull( entity, "entity is required" );
+
+        final UUID colName = entity.getVersion();
+        final UUID entityId = entity.getUuid();
+
+        final Optional<Entity> colValue = entity.getEntity();
+
+        return doWrite( entityId, new RowOp() {
+            @Override
+            public void doOp( final ColumnListMutation<UUID> colMutation ) {
+                colMutation.putColumn( colName, SER.toByteBuffer( colValue ) );
+            }
+        } );
+    }
+
+
+    @Override
+    public MvccEntity load( final CollectionContext context, final UUID entityId, final UUID version )
+            throws ConnectionException {
+        Preconditions.checkNotNull( context, "context is required" );
+        Preconditions.checkNotNull( entityId, "entity id is required" );
+        Preconditions.checkNotNull( version, "version is required" );
+
+
+        final UUID start = null;
+        final UUID end = null;
+
+        Column<UUID> column;
+
+        try {
+            column = keyspace.prepareQuery( CF_ENTITY_DATA ).getKey( entityId )
+                              .getColumn( version ).execute().getResult();
+        }
+
+        catch ( NotFoundException e ) {
+            //swallow, there's just no column
+            return null;
+        }
+
+        if ( column == null ) {
+            return null;
+        }
+
+
+        return new MvccEntityImpl( context, entityId, version, column.getValue(SER) );
+
+    }
+
+
+    @Override
+    public List<MvccEntity> load( final CollectionContext context, final UUID entityId, final UUID version,
+                                  final int maxSize ) {
+
+        Preconditions.checkNotNull( context, "context is required" );
+        Preconditions.checkNotNull( entityId, "entity id is required" );
+        Preconditions.checkNotNull( version, "version is required" );
+        Preconditions.checkArgument( maxSize > 0, "max Size must be greater than 0" );
+
+
+        ColumnList<UUID> columns;
+
+        try {
+
+
+            columns = keyspace.prepareQuery( CF_ENTITY_DATA ).getKey( entityId )
+                              .withColumnRange( version, null, false, maxSize ).execute().getResult();
+        }
+
+        catch ( ConnectionException e ) {
+            throw new RuntimeException( "Unable to load column data", e );
+        }
+
+        if ( columns == null ) {
+            return Collections.EMPTY_LIST;
+        }
+
+        List<MvccEntity> results = new ArrayList<MvccEntity>( columns.size() );
+
+        for ( Column<UUID> col : columns ) {
+            results.add( new MvccEntityImpl( context, entityId, col.getName(), col.getValue( SER ) ) );
+        }
+
+        return results;
+    }
+
+
+    @Override
+    public MutationBatch clear( final CollectionContext context, final UUID entityId, final UUID version ) {
+        Preconditions.checkNotNull( context, "context is required" );
+        Preconditions.checkNotNull( entityId, "entity id is required" );
+        Preconditions.checkNotNull( version, "version is required" );
+
+        final Optional<Entity> value = Optional.absent();
+
+        return doWrite( entityId, new RowOp() {
+            @Override
+            public void doOp( final ColumnListMutation<UUID> colMutation ) {
+                colMutation.putColumn( version, SER.toByteBuffer( value ) );
+            }
+        } );
+    }
+
+
+    @Override
+    public MutationBatch delete( final CollectionContext context, final UUID entityId, final UUID version ) {
+
+        return doWrite( entityId, new RowOp() {
+            @Override
+            public void doOp( final ColumnListMutation<UUID> colMutation ) {
+                colMutation.deleteColumn( version );
+            }
+        } );
+    }
+
+
+    @Override
+    public Collection<CollectionColumnFamily> getColumnFamilies() {
+
+        //create the CF entity data.  We want it reversed b/c we want the most recent version at the top of the
+        //row for fast seeks
+        CollectionColumnFamily cf = new CollectionColumnFamily( CF_ENTITY_DATA,
+                ReversedType.class.getName() + "(" + UUIDType.class.getName() + ")", true );
+
+
+        return Collections.singleton( cf );
+    }
+
+
+    /**
+     * Do the write on the correct row for the entity id with the operation
+     */
+    private MutationBatch doWrite( UUID entityId, RowOp op ) {
+        final MutationBatch batch = keyspace.prepareMutationBatch();
+
+        op.doOp( batch.withRow( CF_ENTITY_DATA, entityId ) );
+
+        return batch;
+    }
+
+
+    /**
+     * Simple callback to perform puts and deletes with a common row setup code
+     */
+    private static interface RowOp {
+
+        /**
+         * The operation to perform on the row
+         */
+        void doOp( ColumnListMutation<UUID> colMutation );
+    }
+
+
+    /**
+     * TODO: Serializer for the entity. This just uses object serialization, change this to use SMILE before production!
+     * We want to retain the Optional wrapper.  It helps us easily mark something as cleaned without removing the data
+     */
+    private static class EntitySerializer extends AbstractSerializer<Optional<Entity>> {
+
+        private static final ObjectSerializer SER = ObjectSerializer.get();
+
+        //the marker for when we're passed a "null" value
+        private static final byte[] EMPTY = new byte[] { 0x0 };
+
+
+        @Override
+        public ByteBuffer toByteBuffer( final Optional<Entity> obj ) {
+
+            //mark this version as empty
+            if ( !obj.isPresent() ) {
+                return ByteBuffer.wrap( EMPTY );
+            }
+
+            return SER.toByteBuffer( obj.get() );
+        }
+
+
+        @Override
+        public Optional<Entity> fromByteBuffer( final ByteBuffer byteBuffer ) {
+
+            final ByteBuffer check = byteBuffer.duplicate();
+
+            if ( check.remaining() == 1 && check.get() == EMPTY[0] ) {
+                return Optional.absent();
+            }
+
+            return Optional.of( ( Entity ) SER.fromByteBuffer( byteBuffer ) );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/ac634a1d/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategy.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategy.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategy.java
new file mode 100644
index 0000000..a249522
--- /dev/null
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/serialization/MvccLogEntrySerializationStrategy.java
@@ -0,0 +1,59 @@
+package org.apache.usergrid.persistence.collection.serialization;
+
+
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.collection.CollectionContext;
+import org.apache.usergrid.persistence.collection.mvcc.entity.MvccLogEntry;
+
+import com.netflix.astyanax.MutationBatch;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+
+
+/** The interface that allows us to serialize a log entry to disk */
+public interface MvccLogEntrySerializationStrategy
+{
+
+    /**
+     * Serialize the entity to the data store with the given collection context
+     *
+     * @param entry the entry to write
+     * @return The mutation batch with the mutation operations for this write.
+     */
+    public MutationBatch write( MvccLogEntry entry );
+
+    /**
+     * Load and return the stage with the given id and a version that is <= the version provided
+     *
+     *
+     * @param context The context to persist the entity into
+     * @param entityId The entity id to load
+     * @param version The version to load.  This will return the version <= the given version
+     *
+     * @return The deserialized version of the entity.  Null if no version <= the current version exists, or the entity does not exist
+     */
+    public MvccLogEntry load( final CollectionContext context, final UUID entityId, final UUID version )
+            throws ConnectionException;
+
+    /**
+     * Load a list, from highest to lowest of the stage with versions <= version up to maxSize elements
+     *
+     * @param context The context to persist the entity into
+     * @param entityId The entity id to load
+     * @param version The max version to seek from
+     * @param maxSize The maximum size to return.  If you receive this size, there may be more versions to load.
+     *
+     * @return A list of entities up to max size ordered from max(UUID)=> min(UUID)
+     */
+    public List<MvccLogEntry> load( CollectionContext context, UUID entityId, UUID version, int maxSize );
+
+    /**
+     * Delete the stage from the context with the given entityId and version
+     *
+     * @param context The context that contains the entity
+     * @param entityId The entity id to delete
+     * @param version The version to delete
+     */
+    public MutationBatch delete( CollectionContext context, UUID entityId, UUID version );
+}