├── .classpath ├── .git-blame-ignore-revs ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── .project ├── .settings └── org.eclipse.jdt.core.prefs ├── LICENSE ├── NOTICE ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── lambdazen │ │ └── bitsy │ │ ├── BitsyEdge.java │ │ ├── BitsyElement.java │ │ ├── BitsyErrorCodes.java │ │ ├── BitsyException.java │ │ ├── BitsyFeatures.java │ │ ├── BitsyGraph.java │ │ ├── BitsyGraphMBean.java │ │ ├── BitsyGraphSONModule.java │ │ ├── BitsyIoRegistryV3d0.java │ │ ├── BitsyIsolationLevel.java │ │ ├── BitsyProperty.java │ │ ├── BitsyRetryException.java │ │ ├── BitsyState.java │ │ ├── BitsyVertex.java │ │ ├── BitsyVertexProperty.java │ │ ├── ICommitChanges.java │ │ ├── IEdge.java │ │ ├── IGraphStore.java │ │ ├── ITransaction.java │ │ ├── PortDatabase.java │ │ ├── ThreadedBitsyGraph.java │ │ ├── UUID.java │ │ ├── UUIDGraphBinarySerializer.java │ │ ├── ads │ │ ├── dict │ │ │ ├── Dictionary.java │ │ │ ├── Dictionary1.java │ │ │ ├── Dictionary11.java │ │ │ ├── Dictionary16.java │ │ │ ├── Dictionary2.java │ │ │ ├── Dictionary3.java │ │ │ ├── Dictionary4.java │ │ │ ├── Dictionary6.java │ │ │ ├── Dictionary8.java │ │ │ ├── DictionaryFactory.java │ │ │ ├── DictionaryMax.java │ │ │ └── PrimitiveDictionary.java │ │ └── set │ │ │ ├── ArraySet.java │ │ │ ├── ClassifierGetter.java │ │ │ ├── CompactMultiSetMax.java │ │ │ ├── CompactSet.java │ │ │ ├── PrimitiveSet.java │ │ │ ├── Set.java │ │ │ ├── Set12.java │ │ │ ├── Set2.java │ │ │ ├── Set24.java │ │ │ ├── Set3.java │ │ │ ├── Set4.java │ │ │ ├── Set6.java │ │ │ ├── Set8.java │ │ │ └── SetMax.java │ │ ├── gremlin │ │ ├── BitsyGraphStep.java │ │ └── BitsyTraversalStrategy.java │ │ ├── index │ │ ├── BitsyIndex.java │ │ ├── BitsyIndexMap.java │ │ ├── EdgeIndex.java │ │ ├── EdgeIndexMap.java │ │ ├── IndexHelper.java │ │ ├── VertexIndex.java │ │ └── VertexIndexMap.java │ │ ├── jsr223 │ │ └── BitsyGremlinPlugin.java │ │ ├── store │ │ ├── AdjacencyMap.java │ │ ├── AdjacencyMapForBeans.java │ │ ├── BackupJob.java │ │ ├── CompactAndCopyTask.java │ │ ├── EdgeBean.java │ │ ├── EdgeBeanJson.java │ │ ├── Endpoint.java │ │ ├── FileBackedMemoryGraphStore.java │ │ ├── IEdgeRemover.java │ │ ├── IStringCanonicalizer.java │ │ ├── ITxBatchJob.java │ │ ├── IVeReorgJob.java │ │ ├── IndexBean.java │ │ ├── JobWithCountDownLatch.java │ │ ├── LoadTask.java │ │ ├── MemoryGraphStore.java │ │ ├── ParallelRecordReader.java │ │ ├── Record.java │ │ ├── RecordReader.java │ │ ├── SingleThreadedStringCanonicalizer.java │ │ ├── TxBatch.java │ │ ├── TxLog.java │ │ ├── TxLogFlushPotential.java │ │ ├── TxUnit.java │ │ ├── VEObsolescencePotential.java │ │ ├── VertexBean.java │ │ └── VertexBeanJson.java │ │ ├── tx │ │ ├── BitsyTransaction.java │ │ └── BitsyTransactionContext.java │ │ ├── util │ │ ├── BitsyElementIterator.java │ │ ├── BufferFlusher.java │ │ ├── BufferPotential.java │ │ ├── BufferQueuer.java │ │ ├── CommittableFileLog.java │ │ ├── DefaultCommitChanges.java │ │ ├── DoubleBuffer.java │ │ ├── DoubleBufferThread.java │ │ ├── DoubleBufferWithExecWork.java │ │ ├── EdgeIterator.java │ │ └── VertexIterator.java │ │ └── wrapper │ │ ├── BitsyAutoReloadingEdge.java │ │ ├── BitsyAutoReloadingGraph.java │ │ └── BitsyAutoReloadingVertex.java └── resources │ └── META-INF │ └── services │ └── org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin └── test ├── java └── com │ └── lambdazen │ └── bitsy │ ├── BitsyGraphIT.java │ ├── BitsyMemGraphIT.java │ ├── FileBasedTestCase.java │ ├── RecoveryTest.java │ ├── ads │ ├── dict │ │ └── DictionaryTest.java │ └── set │ │ ├── CompactMultiSetMaxTest.java │ │ └── SetTest.java │ ├── store │ ├── EndpointTest.java │ ├── FileBackedMemoryGraphStoreTest.java │ ├── MemoryGraphStoreTest.java │ ├── RecordTest.java │ └── SingleThreadedStringCanonicalizerTest.java │ ├── structure │ ├── BitsyGraphStructureTestSuite.java │ ├── BitsyProcessStandardTestSuite.java │ ├── BitsyTestGraphProvider.java │ └── HasLabelTest.java │ └── util │ ├── CommittableFileLogTest.java │ └── DoubleBufferIT.java └── resources ├── com └── lambdazen │ └── bitsy │ └── util │ └── mobydick.txt ├── gremlin-server ├── bitsy.properties └── gremlin-server-bitsy.yaml └── recovery ├── stage1 ├── eA.txt ├── eB.txt ├── metaA.txt ├── metaB.txt ├── txA.txt ├── txB.txt ├── vA.txt └── vB.txt ├── stage2 ├── eA.txt ├── eB.txt ├── metaA.txt ├── metaB.txt ├── txA.txt ├── txB.txt ├── vA.txt └── vB.txt ├── stage3 ├── eA.txt ├── eB.txt ├── metaA.txt ├── metaB.txt ├── txA.txt ├── txB.txt ├── vA.txt └── vB.txt ├── stage4 ├── eA.txt ├── eB.txt ├── metaA.txt ├── metaB.txt ├── txA.txt ├── txB.txt ├── vA.txt └── vB.txt ├── stage5 ├── eA.txt ├── eB.txt ├── metaA.txt ├── metaB.txt ├── txA.txt ├── txB.txt ├── vA.txt └── vB.txt ├── stage6 ├── eA.txt ├── eB.txt ├── metaA.txt ├── metaB.txt ├── txA.txt ├── txB.txt ├── vA.txt └── vB.txt └── stage7 ├── eA.txt ├── eB.txt ├── metaA.txt ├── metaB.txt ├── txA.txt ├── txB.txt ├── vA.txt └── vB.txt /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # reformat with Maveniverse parent 39 2 | 641fdf861487e91b277fda8f06cc852842d14f60 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | - package-ecosystem: "maven" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | interval: "daily" 13 | 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | name: Verify 12 | uses: maveniverse/parent/.github/workflows/ci.yml@release-39 13 | with: 14 | maven-test-run: false # ITs are currently busted and needs to go to separate module 15 | jdk-matrix: '[ "8", "17", "21", "24" ]' 16 | maven-matrix: '[ "3.9.9" ]' 17 | maven-test: './mvnw clean verify -e -B -V -P run-its' 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | *.iml 3 | .idea 4 | *~ 5 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | bitsy 3 | Bitsy v3 is a small, fast, embeddable, durable in-memory graph database that is compatible with Tinkerpop3. NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse. 4 | 5 | 6 | 7 | org.eclipse.jdt.core.javabuilder 8 | 9 | 10 | 11 | org.eclipse.jdt.core.javanature 12 | 13 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Thu Nov 02 18:34:43 EDT 2017 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 3 | eclipse.preferences.version=1 4 | org.eclipse.jdt.core.compiler.source=1.8 5 | org.eclipse.jdt.core.compiler.compliance=1.8 6 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | This product depends on the following components: 2 | 3 | - Tinkerpop Gremlin (gremlin-core, gremlin-test) 4 | http://tinkerpop.apache.org/ 5 | Apache 2.0 license 6 | Copyright (c) 2015- The Apache Software Foundation. 7 | 8 | - Jackson JSON processor (jackson-core, jackson-databind) 9 | https://github.com/FasterXML/ 10 | Apache 2.0 license 11 | Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi 12 | 13 | - Ness Computing Core Component (components-ness-core) 14 | https://github.com/NessComputing/components-ness-core 15 | Apache 2.0 license 16 | Copyright (C) 2012- Ness Computing, Inc. 17 | 18 | - SLF4J API (slf4j-api) 19 | http://www.slf4j.org/ 20 | MIT license 21 | Copyright (c) 2004- QOS.ch 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Maven Central](https://img.shields.io/maven-central/v/com.lambdazen.bitsy/bitsy.svg?label=Maven%20Central)](https://search.maven.org/artifact/com.lambdazen.bitsy/bitsy) 2 | 3 | Bitsy is a small, fast, embeddable, durable in-memory graph database that is compatible with Tinkerpop3. 4 | 5 | [The project Wiki](https://github.com/lambdazen/bitsy/wiki) is the official source of documentation. The original version of the database compatible with Tinkerpop2 is available at https://bitbucket.org/lambdazen/bitsy. 6 | 7 | ### Git branching strategy 8 | 9 | Tags are named release-[version]. Versions start with 3.0. For e.g., release-3.0 10 | 11 | Development happens on `master` branch. 12 | 13 | ## Building it 14 | 15 | The project **build time requirement** is [Apache Maven](https://maven.apache.org/), at least version 3.9 and Java 21. 16 | The project **run time requirement is Java 8**. 17 | 18 | For quick build (runs no tests nor any other plugin like javadoc) 19 | 20 | ``` 21 | mvn clean install -Dtest=void 22 | ``` 23 | 24 | For UT-only build (will run UTs too) 25 | 26 | ``` 27 | mvn clean install 28 | ``` 29 | 30 | For full build (will run UTs and ITs) 31 | 32 | ``` 33 | mvn clean install -P run-its 34 | ``` 35 | 36 | For publishing setup, see [parent POM](https://github.com/maveniverse/parent). -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/BitsyEdge.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import com.lambdazen.bitsy.ads.dict.Dictionary; 4 | import com.lambdazen.bitsy.store.EdgeBean; 5 | import com.lambdazen.bitsy.store.EdgeBeanJson; 6 | import com.lambdazen.bitsy.tx.BitsyTransaction; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.Iterator; 11 | import org.apache.tinkerpop.gremlin.structure.Direction; 12 | import org.apache.tinkerpop.gremlin.structure.Edge; 13 | import org.apache.tinkerpop.gremlin.structure.Property; 14 | import org.apache.tinkerpop.gremlin.structure.Vertex; 15 | import org.apache.tinkerpop.gremlin.structure.util.StringFactory; 16 | 17 | public class BitsyEdge extends BitsyElement implements Edge, IEdge { 18 | UUID outVertexId; 19 | UUID inVertexId; 20 | 21 | public BitsyEdge( 22 | UUID id, 23 | Dictionary properties, 24 | BitsyTransaction tx, 25 | BitsyState state, 26 | int version, 27 | String label, 28 | UUID outVertexId, 29 | UUID inVertexId) { 30 | super(id, label, properties, tx, state, version); 31 | 32 | if (label == null) { 33 | throw new IllegalArgumentException("Edge label can not be null"); // Enforced by 2.3.0 test case 34 | } 35 | 36 | this.outVertexId = outVertexId; 37 | this.inVertexId = inVertexId; 38 | } 39 | 40 | public BitsyEdge(EdgeBean bean, BitsyTransaction tx, BitsyState state) { 41 | this( 42 | bean.getId(), 43 | bean.getPropertiesDict(), 44 | tx, 45 | state, 46 | bean.getVersion(), 47 | bean.getLabel(), 48 | bean.getOutVertexId(), 49 | bean.getInVertexId()); 50 | } 51 | 52 | public EdgeBeanJson asJsonBean() { 53 | // The TX is usually not active at this point. So no checks. 54 | return new EdgeBeanJson((UUID) id, properties, version, label, outVertexId, inVertexId, state); 55 | } 56 | 57 | @Override 58 | public Iterator vertices(Direction dir) { 59 | tx.validateForQuery(this); 60 | 61 | if (dir != Direction.BOTH) { 62 | Vertex ans = inOrOutVertex(dir); 63 | 64 | return Collections.singleton(ans).iterator(); 65 | } else { 66 | return bothVertices(); 67 | } 68 | } 69 | 70 | @Override 71 | public Vertex inVertex() { 72 | tx.validateForQuery(this); 73 | 74 | return inOrOutVertex(Direction.IN); 75 | } 76 | 77 | @Override 78 | public Vertex outVertex() { 79 | tx.validateForQuery(this); 80 | 81 | return inOrOutVertex(Direction.OUT); 82 | } 83 | 84 | @Override 85 | public Iterator bothVertices() { 86 | tx.validateForQuery(this); 87 | 88 | Vertex inV = inVertex(); 89 | Vertex outV = outVertex(); 90 | return Arrays.asList(new Vertex[] {outV, inV}).iterator(); 91 | } 92 | 93 | private Vertex inOrOutVertex(Direction dir) { 94 | Vertex ans = tx.getVertex(getVertexId(dir)); 95 | 96 | // Vertex may disappear in READ_COMMITTED MODE 97 | if (ans == null) { 98 | throw new BitsyRetryException( 99 | BitsyErrorCodes.CONCURRENT_MODIFICATION, 100 | "The vertex in direction " + dir + " of the edge " + this.id() 101 | + " was removed by another transaction"); 102 | } 103 | 104 | return ans; 105 | } 106 | 107 | public UUID getInVertexId() { 108 | return inVertexId; 109 | } 110 | 111 | public UUID getOutVertexId() { 112 | return outVertexId; 113 | } 114 | 115 | public UUID getVertexId(Direction dir) { 116 | if (dir == Direction.IN) { 117 | return inVertexId; 118 | } else if (dir == Direction.OUT) { 119 | return outVertexId; 120 | } else { 121 | throw new IllegalArgumentException("Unsupported direction " + dir); 122 | } 123 | } 124 | 125 | public void incrementVersion() { 126 | this.version++; 127 | } 128 | 129 | public void remove() { 130 | tx.removeEdge(this); 131 | } 132 | 133 | public String toString() { 134 | return StringFactory.edgeString(this); 135 | } 136 | 137 | @Override 138 | // THERE ARE TWO MORE COPIES OF THIS CODE IN ELEMENT AND VERTEX 139 | public Iterator> properties(String... propertyKeys) { 140 | ArrayList> ans = new ArrayList>(); 141 | 142 | if (propertyKeys.length == 0) { 143 | if (this.properties == null) return Collections.emptyIterator(); 144 | propertyKeys = this.properties.getPropertyKeys(); 145 | } 146 | 147 | for (String key : propertyKeys) { 148 | Property prop = property(key); 149 | if (prop.isPresent()) ans.add(prop); 150 | } 151 | return ans.iterator(); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/BitsyException.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | public class BitsyException extends RuntimeException { 4 | private static final long serialVersionUID = -5310572247323732287L; 5 | BitsyErrorCodes code; 6 | 7 | public BitsyException(BitsyErrorCodes code) { 8 | super(code.toString()); 9 | 10 | this.code = code; 11 | } 12 | 13 | public BitsyException(BitsyErrorCodes code, String s) { 14 | super(code.toString() + ". " + s); 15 | 16 | this.code = code; 17 | } 18 | 19 | public BitsyException(BitsyErrorCodes code, String s, Throwable t) { 20 | super(code.toString() + ". " + s, t); 21 | 22 | this.code = code; 23 | } 24 | 25 | public BitsyErrorCodes getErrorCode() { 26 | return code; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/BitsyGraphMBean.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | public interface BitsyGraphMBean { 4 | /** 5 | * Returns the reorgFactor which typically determines when the V?.txt and 6 | * E?.txt file will be reorganized. Reorganization is triggered only when the 7 | * total number of new vertices and edges added is more than the factor 8 | * multiplied by the original number of vertices and edges. Default value is 1. 9 | */ 10 | public double getReorgFactor(); 11 | 12 | /** 13 | * Set the reorgFactor. A higher number indicates fewer file operations, but 14 | * more disk space and startup time in the worst case. Default value is 1. 15 | */ 16 | public void setReorgFactor(double factor); 17 | 18 | /** 19 | * Returns the minimum number of vertices and edges that must be added 20 | * before a reorganization is considered. This rule is used in combination 21 | * with the reorgFactor. Default value is 1000. 22 | */ 23 | public int getMinLinesPerReorg(); 24 | 25 | /** 26 | * Modify the minimum lines to be added before a reorganization is 27 | * considered. Default value is 1000. 28 | */ 29 | public void setMinLinesPerReorg(int minLinesPerReorg); 30 | 31 | /** 32 | * Returns the transaction log threshold which is the minimum size of the 33 | * transaction log (T?.txt) in bytes, before which the contents of the log 34 | * are copied to V?.txt and E?.txt. Default value is 4MB. 35 | */ 36 | public long getTxLogThreshold(); 37 | 38 | /** 39 | * Modify the transaction log threshold. A higher number indicates fewer 40 | * file operations, but more disk space and startup time in the worst case. 41 | * Default value is 4MB. 42 | */ 43 | public void setTxLogThreshold(long txLogThreshold); 44 | 45 | /** This method flushes the transaction log to the V/E text files */ 46 | public void flushTxLog(); 47 | 48 | /** This method backs up the database while it is still operational. Only one backup can be in progress at a time. 49 | * 50 | * @param pathToDir directory to which the database must be backed up. 51 | */ 52 | public void backup(String pathToDir); 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/BitsyGraphSONModule.java: -------------------------------------------------------------------------------- 1 | // Copyright 2017 JanusGraph Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.lambdazen.bitsy; 16 | 17 | import com.lambdazen.bitsy.store.EdgeBean; 18 | import com.lambdazen.bitsy.store.VertexBean; 19 | import java.io.IOException; 20 | import java.util.Collections; 21 | import java.util.LinkedHashMap; 22 | import java.util.Map; 23 | import org.apache.tinkerpop.gremlin.structure.io.graphson.TinkerPopJacksonModule; 24 | import org.apache.tinkerpop.shaded.jackson.core.*; 25 | import org.apache.tinkerpop.shaded.jackson.core.type.WritableTypeId; 26 | import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext; 27 | import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider; 28 | import org.apache.tinkerpop.shaded.jackson.databind.deser.std.StdDeserializer; 29 | import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer; 30 | import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer; 31 | 32 | /** 33 | * @author Stephen Mallette (http://stephen.genoprime.com) 34 | */ 35 | public class BitsyGraphSONModule extends TinkerPopJacksonModule { 36 | 37 | private static final String TYPE_NAMESPACE = "bitsy"; 38 | 39 | private static final Map TYPE_DEFINITIONS = 40 | Collections.unmodifiableMap(new LinkedHashMap() { 41 | { 42 | put(UUID.class, "UUID"); 43 | put(VertexBean.class, "VertexBean"); 44 | put(EdgeBean.class, "EdgeBean"); 45 | } 46 | }); 47 | 48 | private BitsyGraphSONModule() { 49 | super("bitsy"); 50 | addSerializer(UUID.class, new UUIDSerializer()); 51 | addDeserializer(UUID.class, new UUIDDeserializer()); 52 | 53 | // addSerializer(VertexBean.class, new UUIDSerializer()); 54 | // addSerializer(EdgeBean.class, new UUIDSerializer()); 55 | } 56 | 57 | private static final BitsyGraphSONModule INSTANCE = new BitsyGraphSONModule(); 58 | 59 | public static final BitsyGraphSONModule getInstance() { 60 | return INSTANCE; 61 | } 62 | 63 | @Override 64 | public Map getTypeDefinitions() { 65 | return TYPE_DEFINITIONS; 66 | } 67 | 68 | @Override 69 | public String getTypeNamespace() { 70 | return TYPE_NAMESPACE; 71 | } 72 | 73 | public static class UUIDSerializer extends StdSerializer { 74 | 75 | public UUIDSerializer() { 76 | super(UUID.class); 77 | } 78 | 79 | @Override 80 | public void serialize( 81 | final UUID uuid, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) 82 | throws IOException, JsonGenerationException { 83 | String uuidStr = UUID.toString(uuid); 84 | jsonGenerator.writeString(uuidStr); 85 | } 86 | 87 | @Override 88 | public void serializeWithType( 89 | final UUID uuid, 90 | final JsonGenerator jsonGenerator, 91 | final SerializerProvider serializerProvider, 92 | final TypeSerializer typeSerializer) 93 | throws IOException, JsonProcessingException { 94 | // since jackson 2.9, must keep track of `typeIdDef` in order to close it properly 95 | final WritableTypeId typeIdDef = 96 | typeSerializer.writeTypePrefix(jsonGenerator, typeSerializer.typeId(uuid, JsonToken.VALUE_STRING)); 97 | String uuidStr = UUID.toString(uuid); 98 | jsonGenerator.writeString(uuidStr); 99 | typeSerializer.writeTypeSuffix(jsonGenerator, typeIdDef); 100 | } 101 | } 102 | 103 | public static class UUIDDeserializer extends StdDeserializer { 104 | public UUIDDeserializer() { 105 | super(UUID.class); 106 | } 107 | 108 | @Override 109 | public UUID deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) 110 | throws IOException, JsonProcessingException { 111 | jsonParser.nextToken(); 112 | final String uuidStr = deserializationContext.readValue(jsonParser, String.class); 113 | return UUID.fromString(uuidStr); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/BitsyIoRegistryV3d0.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import com.lambdazen.bitsy.store.EdgeBean; 4 | import com.lambdazen.bitsy.store.VertexBean; 5 | import org.apache.tinkerpop.gremlin.structure.io.AbstractIoRegistry; 6 | import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryIo; 7 | import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONIo; 8 | import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoIo; 9 | import org.apache.tinkerpop.shaded.kryo.Kryo; 10 | import org.apache.tinkerpop.shaded.kryo.Serializer; 11 | import org.apache.tinkerpop.shaded.kryo.io.Input; 12 | import org.apache.tinkerpop.shaded.kryo.io.Output; 13 | 14 | public class BitsyIoRegistryV3d0 extends AbstractIoRegistry { 15 | private static final BitsyIoRegistryV3d0 INSTANCE = new BitsyIoRegistryV3d0(); 16 | 17 | private BitsyIoRegistryV3d0() { 18 | register(GryoIo.class, UUID.class, new UUIDGryoSerializer()); 19 | register(GryoIo.class, VertexBean.class, new UUIDGryoSerializer()); 20 | register(GryoIo.class, EdgeBean.class, new UUIDGryoSerializer()); 21 | 22 | register(GraphSONIo.class, UUID.class, BitsyGraphSONModule.getInstance()); 23 | register(GraphSONIo.class, VertexBean.class, BitsyGraphSONModule.getInstance()); 24 | register(GraphSONIo.class, EdgeBean.class, BitsyGraphSONModule.getInstance()); 25 | 26 | register(GraphBinaryIo.class, UUID.class, new UUIDGraphBinarySerializer()); 27 | register(GraphBinaryIo.class, VertexBean.class, new UUIDGraphBinarySerializer()); 28 | register(GraphBinaryIo.class, EdgeBean.class, new UUIDGraphBinarySerializer()); 29 | } 30 | 31 | public static BitsyIoRegistryV3d0 instance() { 32 | return INSTANCE; 33 | } 34 | 35 | static final class UUIDGryoSerializer extends Serializer { 36 | @Override 37 | public void write(final Kryo kryo, final Output output, final UUID uuid) { 38 | output.writeLong(uuid.getMostSignificantBits()); 39 | output.writeLong(uuid.getLeastSignificantBits()); 40 | } 41 | 42 | @Override 43 | public UUID read(final Kryo kryo, final Input input, final Class aClass) { 44 | long msb = input.readLong(); 45 | long lsb = input.readLong(); 46 | return new UUID(msb, lsb); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/BitsyIsolationLevel.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | public enum BitsyIsolationLevel { 4 | READ_COMMITTED, // default 5 | REPEATABLE_READ 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/BitsyProperty.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import java.util.NoSuchElementException; 4 | import org.apache.tinkerpop.gremlin.structure.Element; 5 | import org.apache.tinkerpop.gremlin.structure.Property; 6 | import org.apache.tinkerpop.gremlin.structure.util.ElementHelper; 7 | import org.apache.tinkerpop.gremlin.structure.util.StringFactory; 8 | 9 | public class BitsyProperty implements Property { 10 | BitsyElement element; 11 | String key; 12 | T value; 13 | boolean removed = false; 14 | 15 | public BitsyProperty(BitsyElement element, String key, T value) { 16 | this.element = element; 17 | this.key = key; 18 | this.value = value; 19 | } 20 | 21 | @Override 22 | public String key() { 23 | return key; 24 | } 25 | 26 | @Override 27 | public T value() throws NoSuchElementException { 28 | if (removed) { 29 | throw new NoSuchElementException("This property is empty"); 30 | } else { 31 | return value; 32 | } 33 | } 34 | 35 | @Override 36 | public boolean isPresent() { 37 | return !removed; 38 | } 39 | 40 | @Override 41 | public Element element() { 42 | return element; 43 | } 44 | 45 | @Override 46 | public void remove() { 47 | if (isPresent()) { 48 | element.removeProperty(key); 49 | this.removed = true; 50 | } 51 | } 52 | 53 | // Moved to ElementHelper hashCode and equals in TP3 54 | @Override 55 | public int hashCode() { 56 | return ElementHelper.hashCode(this); 57 | } 58 | 59 | @Override 60 | public boolean equals(final Object object) { 61 | return ElementHelper.areEqual(this, object); 62 | } 63 | 64 | public String toString() { 65 | return StringFactory.propertyString(this); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/BitsyRetryException.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | public class BitsyRetryException extends BitsyException { 4 | private static final long serialVersionUID = 976641612846833462L; 5 | BitsyErrorCodes code; 6 | 7 | public BitsyRetryException(BitsyErrorCodes code) { 8 | super(code); 9 | } 10 | 11 | public BitsyRetryException(BitsyErrorCodes code, String s) { 12 | super(code, s); 13 | } 14 | 15 | public BitsyRetryException(BitsyErrorCodes code, String s, Throwable t) { 16 | super(code, s, t); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/BitsyState.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | public enum BitsyState { 4 | U, // unmodified 5 | M, // modified 6 | D // deleted 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/BitsyVertexProperty.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import java.util.Collections; 4 | import java.util.Iterator; 5 | import java.util.Set; 6 | import org.apache.tinkerpop.gremlin.structure.Property; 7 | import org.apache.tinkerpop.gremlin.structure.Vertex; 8 | import org.apache.tinkerpop.gremlin.structure.VertexProperty; 9 | import org.apache.tinkerpop.gremlin.structure.util.StringFactory; 10 | 11 | public class BitsyVertexProperty extends BitsyProperty implements VertexProperty { 12 | public BitsyVertexProperty(final BitsyVertex vertex, final String key, final V value) { 13 | super(vertex, key, value); 14 | } 15 | 16 | @Override 17 | public Set keys() { 18 | return Collections.emptySet(); 19 | } 20 | 21 | @Override 22 | public Property property(final String key) { 23 | throw new BitsyException(BitsyErrorCodes.NO_META_PROPERTY_SUPPORT); 24 | } 25 | 26 | @Override 27 | public Property property(final String key, final U value) { 28 | throw new BitsyException(BitsyErrorCodes.NO_META_PROPERTY_SUPPORT); 29 | } 30 | 31 | @Override 32 | public Vertex element() { 33 | return (BitsyVertex) super.element(); 34 | } 35 | 36 | @Override 37 | public Iterator> properties(final String... propertyKeys) { 38 | throw new BitsyException(BitsyErrorCodes.NO_META_PROPERTY_SUPPORT); 39 | } 40 | 41 | @Override 42 | public Object id() { 43 | return element().id().toString() + ":" + key(); 44 | } 45 | 46 | public String toString() { 47 | return StringFactory.propertyString(this); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ICommitChanges.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import java.util.Collection; 4 | 5 | public interface ICommitChanges { 6 | public Collection getVertexChanges(); 7 | 8 | public Collection getEdgeChanges(); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/IEdge.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | public interface IEdge { 4 | public UUID getInVertexId(); 5 | 6 | public UUID getOutVertexId(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/IGraphStore.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import com.lambdazen.bitsy.store.EdgeBean; 4 | import com.lambdazen.bitsy.store.VertexBean; 5 | import com.lambdazen.bitsy.tx.BitsyTransaction; 6 | import java.util.Collection; 7 | import java.util.List; 8 | import java.util.Set; 9 | import org.apache.tinkerpop.gremlin.structure.Direction; 10 | import org.apache.tinkerpop.gremlin.structure.Element; 11 | 12 | public interface IGraphStore { 13 | public void commit(ICommitChanges changes); 14 | 15 | /** Only to be used internally within the store */ 16 | public VertexBean getVertex(UUID id); 17 | 18 | /** Returns a transaction-specific BitsyVertex given the tx and the ID */ 19 | public BitsyVertex getBitsyVertex(BitsyTransaction tx, UUID id); 20 | 21 | /** Only to be used internally within the store */ 22 | public EdgeBean getEdge(UUID id); 23 | 24 | /** Returns a transaction-specific BitsyEdge given the tx and the ID */ 25 | public BitsyEdge getBitsyEdge(BitsyTransaction tx, UUID id); 26 | 27 | public List getEdges(UUID vertexId, Direction dir, String[] edgeLabels); 28 | 29 | public Collection getAllVertices(); 30 | 31 | public Collection getAllEdges(); 32 | 33 | public void createKeyIndex(String key, Class elementType); 34 | 35 | public void dropKeyIndex(String key, Class elementType); 36 | 37 | public Set getIndexedKeys(Class elementType); 38 | 39 | public void shutdown(); 40 | 41 | public Collection lookupVertices(String key, Object value); 42 | 43 | public Collection lookupEdges(String key, Object value); 44 | 45 | public boolean allowFullGraphScans(); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ITransaction.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import java.util.Iterator; 4 | import org.apache.tinkerpop.gremlin.structure.Direction; 5 | import org.apache.tinkerpop.gremlin.structure.Edge; 6 | import org.apache.tinkerpop.gremlin.structure.Transaction; 7 | import org.apache.tinkerpop.gremlin.structure.Vertex; 8 | 9 | public interface ITransaction extends Transaction { 10 | public void save(boolean commit); 11 | 12 | public void validateForQuery(BitsyElement bitsyElement) throws BitsyException; 13 | 14 | public Vertex getVertex(UUID outVertexId) throws BitsyException; 15 | 16 | public Edge getEdge(UUID id) throws BitsyException; 17 | 18 | public Iterable getEdges(BitsyVertex bitsyVertex, Direction dir, String... edgeLabels) throws BitsyException; 19 | 20 | public void markForPropertyUpdate(BitsyElement bitsyElement) throws BitsyException; 21 | 22 | public void addVertex(BitsyVertex vertex) throws BitsyException; 23 | 24 | public void removeVertex(BitsyVertex vertex) throws BitsyException; 25 | 26 | public void addEdge(BitsyEdge edge) throws BitsyException; 27 | 28 | public void removeEdge(BitsyEdge edge) throws BitsyException; 29 | 30 | public Iterator getAllVertices(); 31 | 32 | public Iterator getAllEdges(); 33 | 34 | public Iterator lookupVertices(String key, Object value); 35 | 36 | public Iterator lookupEdges(String key, Object value); 37 | 38 | public BitsyIsolationLevel getIsolationLevel(); 39 | 40 | public void setIsolationLevel(BitsyIsolationLevel level); 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ThreadedBitsyGraph.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import com.lambdazen.bitsy.tx.BitsyTransaction; 4 | import com.lambdazen.bitsy.tx.BitsyTransactionContext; 5 | 6 | public class ThreadedBitsyGraph extends BitsyGraph { 7 | BitsyGraph underlyingGraph; 8 | BitsyTransaction tx; 9 | 10 | public ThreadedBitsyGraph(BitsyGraph g) { 11 | // Using protected constructor that doesn't create a graph store 12 | super('_', g.isFullGraphScanAllowed()); 13 | 14 | this.underlyingGraph = g; 15 | this.tx = null; 16 | } 17 | 18 | public String toString() { 19 | return underlyingGraph.toString(); 20 | } 21 | 22 | @Override 23 | public Features features() { 24 | return underlyingGraph.features(); 25 | } 26 | 27 | @Override 28 | protected BitsyTransaction getTx() { 29 | // Overriding the getTx() method ensures that the work will be done on 30 | // the local transaction, NOT the ThreadLocal transaction 31 | if ((tx == null) || (!tx.isOpen())) { 32 | this.tx = new BitsyTransaction( 33 | new BitsyTransactionContext(underlyingGraph.getStore()), 34 | getDefaultIsolationLevel(), 35 | underlyingGraph); 36 | } 37 | 38 | return tx; 39 | } 40 | 41 | @Override 42 | /** This method can be used to check if the current threaded-graph is actively executing a transaction */ 43 | public boolean isTransactionActive() { 44 | return (tx != null); 45 | } 46 | 47 | public BitsyIsolationLevel getDefaultIsolationLevel() { 48 | return underlyingGraph.getDefaultIsolationLevel(); 49 | } 50 | 51 | public void setDefaultIsolationLevel(BitsyIsolationLevel level) { 52 | underlyingGraph.setDefaultIsolationLevel(level); 53 | } 54 | 55 | public BitsyIsolationLevel getTxIsolationLevel() { 56 | return getTx().getIsolationLevel(); 57 | } 58 | 59 | public void setTxIsolationLevel(BitsyIsolationLevel level) { 60 | getTx().setIsolationLevel(level); 61 | } 62 | 63 | @Override 64 | public void shutdown() { 65 | // As per Blueprints tests, shutdown() implies automatic commit 66 | if (tx == null) { 67 | // Nothing to do 68 | } else { 69 | try { 70 | // Stop the old transaction if it exists 71 | tx.commit(); 72 | } finally { 73 | // Remove this transaction -- independent of success/failure 74 | this.tx = null; 75 | } 76 | } 77 | 78 | // Don't mess with the graph store -- this is only a ThreadedGraph, not the main one 79 | } 80 | 81 | // @Deprecated 82 | // public void stopTransaction(Conclusion conclusion) { 83 | // stopTx(conclusion == Conclusion.SUCCESS); 84 | // } 85 | 86 | // @Override 87 | // public void commit() { 88 | // tx.save(commit); 89 | // } 90 | // 91 | // @Override 92 | // public void rollback() { 93 | // stopTx(false); 94 | // } 95 | // 96 | // public void stopTx(boolean commit) { 97 | // if (tx == null) { 98 | // // Nothing to do 99 | // } else { 100 | // try { 101 | // // Stop the old transaction if it exists 102 | // tx.save(commit); 103 | // } finally { 104 | // // Remove this transaction -- independent of success/failure 105 | // this.tx = null; 106 | // } 107 | // } 108 | // } 109 | // 110 | // @Override 111 | // public TransactionalGraph startTransaction() { 112 | // throw new UnsupportedOperationException("Can not startTransaction on a threaded transaction graph"); 113 | // } 114 | // 115 | // @Override 116 | // public void shutdown() { 117 | // // As per Blueprints tests, shutdown() implies automatic commit 118 | // stopTx(true); 119 | // 120 | // // Don't mess with the graph store -- this is only a ThreadedGraph, not the main one 121 | // } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/UUID.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | 5 | /** This class captures a UUID and is modeled after java.util.UUID */ 6 | public class UUID implements Comparable { 7 | // Java guarantees that 8 | // "Reads and writes are atomic for reference variables and for most primitive variables (all types except long and 9 | // double)." 10 | // Therefore, mostSigBits and leastSigBits must not be changed during the lifetime of this object. 11 | // Also, these objects must not be accessible to other threads without a memory flush/fence/barrier 12 | // Typically this memory barrier is handled by the ConcurrentHashMap that holds the vertex/edge bean. 13 | private final long mostSigBits; 14 | private final long leastSigBits; 15 | 16 | public UUID(long msb, long lsb) { 17 | this.mostSigBits = msb; 18 | this.leastSigBits = lsb; 19 | } 20 | 21 | @JsonIgnore 22 | public long getMostSignificantBits() { 23 | return mostSigBits; 24 | } 25 | 26 | @JsonIgnore 27 | public long getLeastSignificantBits() { 28 | return leastSigBits; 29 | } 30 | 31 | public String toString() { 32 | return uuidRepr(); 33 | } 34 | 35 | public String uuidRepr() { 36 | return new java.util.UUID(mostSigBits, leastSigBits).toString(); 37 | } 38 | 39 | public static UUID fromString(String str) { 40 | java.util.UUID ans = java.util.UUID.fromString(str); 41 | 42 | return new UUID(ans.getMostSignificantBits(), ans.getLeastSignificantBits()); 43 | } 44 | 45 | public static UUID randomUUID() { 46 | java.util.UUID ans = java.util.UUID.randomUUID(); 47 | 48 | return new UUID(ans.getMostSignificantBits(), ans.getLeastSignificantBits()); 49 | } 50 | 51 | public int compareTo(UUID other) { 52 | if (this.mostSigBits < other.mostSigBits) { 53 | return -1; 54 | } else if (this.mostSigBits > other.mostSigBits) { 55 | return 1; 56 | } else if (this.leastSigBits < other.leastSigBits) { 57 | return -1; 58 | } else if (this.leastSigBits > other.leastSigBits) { 59 | return 1; 60 | } else { 61 | return 0; 62 | } 63 | } 64 | 65 | @Override 66 | public int hashCode() { 67 | // Same as java.util.UUID 68 | long hilo = mostSigBits ^ leastSigBits; 69 | return ((int) (hilo >> 32)) ^ (int) hilo; 70 | } 71 | 72 | @Override 73 | public boolean equals(Object obj) { 74 | if (obj == null) { 75 | return false; 76 | } else if (this == obj) { 77 | return true; 78 | } else { 79 | try { 80 | UUID other = (UUID) obj; 81 | return (mostSigBits == other.getMostSignificantBits()) 82 | && (leastSigBits == other.getLeastSignificantBits()); 83 | } catch (ClassCastException e) { 84 | return false; 85 | } 86 | } 87 | } 88 | 89 | public static String toString(UUID obj) { 90 | return new java.util.UUID(obj.getMostSignificantBits(), obj.getLeastSignificantBits()).toString(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/UUIDGraphBinarySerializer.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import java.io.IOException; 4 | import org.apache.tinkerpop.gremlin.structure.io.Buffer; 5 | import org.apache.tinkerpop.gremlin.structure.io.binary.DataType; 6 | import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; 7 | import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; 8 | import org.apache.tinkerpop.gremlin.structure.io.binary.types.CustomTypeSerializer; 9 | 10 | public class UUIDGraphBinarySerializer implements CustomTypeSerializer { 11 | 12 | private final byte[] typeInfoBuffer = new byte[] {0, 0, 0, 0}; 13 | 14 | @Override 15 | public String getTypeName() { 16 | return "bitsy.UUID"; 17 | } 18 | 19 | @Override 20 | public DataType getDataType() { 21 | return DataType.CUSTOM; 22 | } 23 | 24 | @Override 25 | public UUID read(Buffer buffer, GraphBinaryReader context) throws IOException { 26 | // {custom type info}, {value_flag} and {value} 27 | // No custom_type_info 28 | if (buffer.readInt() != 0) { 29 | throw new IOException("{custom_type_info} should not be provided for this custom type"); 30 | } 31 | 32 | return readValue(buffer, context, true); 33 | } 34 | 35 | @Override 36 | public UUID readValue(Buffer buffer, GraphBinaryReader context, boolean nullable) throws IOException { 37 | if (nullable) { 38 | final byte valueFlag = buffer.readByte(); 39 | if ((valueFlag & 1) == 1) { 40 | return null; 41 | } 42 | } 43 | 44 | // Read the byte length of the value bytes 45 | final int valueLength = buffer.readInt(); 46 | 47 | if (valueLength <= 0) { 48 | throw new IOException(String.format("Unexpected value length: %d", valueLength)); 49 | } 50 | 51 | if (valueLength > buffer.readableBytes()) { 52 | throw new IOException( 53 | String.format("Not enough readable bytes: %d (expected %d)", valueLength, buffer.readableBytes())); 54 | } 55 | 56 | long msb = context.readValue(buffer, Long.class, false); 57 | long lsb = context.readValue(buffer, Long.class, false); 58 | return new UUID(msb, lsb); 59 | } 60 | 61 | @Override 62 | public void write(UUID value, Buffer buffer, GraphBinaryWriter context) throws IOException { 63 | // Write {custom type info}, {value_flag} and {value} 64 | buffer.writeBytes(typeInfoBuffer); 65 | 66 | writeValue(value, buffer, context, true); 67 | } 68 | 69 | @Override 70 | public void writeValue(UUID value, Buffer buffer, GraphBinaryWriter context, boolean nullable) throws IOException { 71 | if (value == null) { 72 | if (!nullable) { 73 | throw new IOException("Unexpected null value when nullable is false"); 74 | } 75 | 76 | context.writeValueFlagNull(buffer); 77 | return; 78 | } 79 | 80 | if (nullable) { 81 | context.writeValueFlagNone(buffer); 82 | } 83 | 84 | final Long msb = value.getMostSignificantBits(); 85 | final Long lsb = value.getLeastSignificantBits(); 86 | 87 | // value_length = name_byte_length + long + long 88 | buffer.writeInt(4 + 8 + 8); 89 | 90 | context.writeValue(msb, buffer, false); 91 | context.writeValue(lsb, buffer, false); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/dict/Dictionary.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.dict; 2 | 3 | import com.lambdazen.bitsy.store.IStringCanonicalizer; 4 | 5 | /** 6 | * This is an re-organizing (not immutable) map from String to Object. The set 7 | * and remove methods return a reference to a new map with the value. 8 | */ 9 | public interface Dictionary { 10 | public int size(); 11 | 12 | public Object getProperty(String key); 13 | 14 | public String[] getPropertyKeys(); 15 | 16 | public Dictionary setProperty(String key, Object value); 17 | 18 | public Dictionary removeProperty(String key); 19 | 20 | public Dictionary copyOf(); 21 | 22 | // public TreeMap toMap(); 23 | 24 | public void canonicalizeKeys(IStringCanonicalizer canonicalizer); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/dict/Dictionary1.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.dict; 2 | 3 | /** This class implements a dictionary with one element */ 4 | public class Dictionary1 extends PrimitiveDictionary implements Dictionary { 5 | public static final int CAPACITY = 1; 6 | 7 | String key0; 8 | Object value0; 9 | 10 | // Expand constructor 11 | public Dictionary1(String key, Object value) { 12 | this.key0 = key; 13 | this.value0 = value; 14 | } 15 | 16 | // Contract constructor 17 | public Dictionary1(Dictionary2 base) { 18 | this.key0 = base.key0; 19 | this.value0 = base.value0; 20 | } 21 | 22 | // Copy constructor 23 | public Dictionary1(Dictionary1 base) { 24 | this.key0 = base.key0; 25 | this.value0 = base.value0; 26 | } 27 | 28 | @Override 29 | protected String[] keys() { 30 | return new String[] {key0}; 31 | } 32 | 33 | @Override 34 | protected Object[] values() { 35 | return new Object[] {value0}; 36 | } 37 | 38 | @Override 39 | public Dictionary copyOf() { 40 | return new Dictionary1(this); 41 | } 42 | 43 | protected int contractThreshold() { 44 | return 0; 45 | } 46 | 47 | protected Dictionary contract() { 48 | return null; 49 | } 50 | 51 | protected Dictionary expand(String key, Object value) { 52 | return new Dictionary2(this, key, value); 53 | } 54 | 55 | protected void write(int index, String key, Object value) { 56 | key0 = key; 57 | value0 = value; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/dict/Dictionary2.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.dict; 2 | 3 | public class Dictionary2 extends PrimitiveDictionary implements Dictionary { 4 | public static final int CAPACITY = 2; 5 | 6 | String key0; 7 | Object value0; 8 | 9 | String key1; 10 | Object value1; 11 | 12 | // Expand constructor 13 | public Dictionary2(Dictionary1 base, String key, Object value) { 14 | this.key0 = base.key0; 15 | this.value0 = base.value0; 16 | 17 | // Last key 18 | this.key1 = key; 19 | this.value1 = value; 20 | } 21 | 22 | // Contract constructor 23 | public Dictionary2(Dictionary3 base) { 24 | this.key0 = base.key0; 25 | this.value0 = base.value0; 26 | 27 | this.key1 = base.key1; 28 | this.value1 = base.value1; 29 | } 30 | 31 | // Copy constructor 32 | public Dictionary2(Dictionary2 base) { 33 | this.key0 = base.key0; 34 | this.value0 = base.value0; 35 | 36 | this.key1 = base.key1; 37 | this.value1 = base.value1; 38 | } 39 | 40 | // FromMap constructor 41 | public Dictionary2(String[] keys, Object[] values) { 42 | this.key0 = lookupKey(keys, 0); 43 | this.value0 = lookupValue(values, 0); 44 | 45 | this.key1 = lookupKey(keys, 1); 46 | this.value1 = lookupValue(values, 1); 47 | } 48 | 49 | @Override 50 | String[] keys() { 51 | return new String[] {key0, key1}; 52 | } 53 | 54 | @Override 55 | Object[] values() { 56 | return new Object[] {value0, value1}; 57 | } 58 | 59 | @Override 60 | void write(int index, String key, Object value) { 61 | switch (index) { 62 | case 0: 63 | key0 = key; 64 | value0 = value; 65 | break; 66 | 67 | case 1: 68 | key1 = key; 69 | value1 = value; 70 | break; 71 | 72 | default: 73 | throw new IllegalArgumentException("Invalid index " + index); 74 | } 75 | } 76 | 77 | @Override 78 | Dictionary expand(String key, Object value) { 79 | return new Dictionary3(this, key, value); 80 | } 81 | 82 | @Override 83 | int contractThreshold() { 84 | return Dictionary1.CAPACITY; 85 | } 86 | 87 | @Override 88 | Dictionary contract() { 89 | return new Dictionary1(this); 90 | } 91 | 92 | @Override 93 | public Dictionary copyOf() { 94 | return new Dictionary2(this); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/dict/Dictionary3.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.dict; 2 | 3 | public class Dictionary3 extends PrimitiveDictionary implements Dictionary { 4 | public static final int CAPACITY = 3; 5 | 6 | String key0; 7 | Object value0; 8 | 9 | String key1; 10 | Object value1; 11 | 12 | String key2; 13 | Object value2; 14 | 15 | // Expand constructor 16 | public Dictionary3(Dictionary2 base, String key, Object value) { 17 | this.key0 = base.key0; 18 | this.value0 = base.value0; 19 | 20 | this.key1 = base.key1; 21 | this.value1 = base.value1; 22 | 23 | // Last key 24 | this.key2 = key; 25 | this.value2 = value; 26 | } 27 | 28 | // Contract constructor 29 | public Dictionary3(Dictionary4 base) { 30 | this.key0 = base.key0; 31 | this.value0 = base.value0; 32 | 33 | this.key1 = base.key1; 34 | this.value1 = base.value1; 35 | 36 | this.key2 = base.key2; 37 | this.value2 = base.value2; 38 | } 39 | 40 | // Copy constructor 41 | public Dictionary3(Dictionary3 base) { 42 | this.key0 = base.key0; 43 | this.value0 = base.value0; 44 | 45 | this.key1 = base.key1; 46 | this.value1 = base.value1; 47 | 48 | this.key2 = base.key2; 49 | this.value2 = base.value2; 50 | } 51 | 52 | // FromMap constructor 53 | public Dictionary3(String[] keys, Object[] values) { 54 | this.key0 = lookupKey(keys, 0); 55 | this.value0 = lookupValue(values, 0); 56 | 57 | this.key1 = lookupKey(keys, 1); 58 | this.value1 = lookupValue(values, 1); 59 | 60 | this.key2 = lookupKey(keys, 2); 61 | this.value2 = lookupValue(values, 2); 62 | } 63 | 64 | @Override 65 | String[] keys() { 66 | return new String[] {key0, key1, key2}; 67 | } 68 | 69 | @Override 70 | Object[] values() { 71 | return new Object[] {value0, value1, value2}; 72 | } 73 | 74 | @Override 75 | void write(int index, String key, Object value) { 76 | switch (index) { 77 | case 0: 78 | key0 = key; 79 | value0 = value; 80 | break; 81 | 82 | case 1: 83 | key1 = key; 84 | value1 = value; 85 | break; 86 | 87 | case 2: 88 | key2 = key; 89 | value2 = value; 90 | break; 91 | 92 | default: 93 | throw new IllegalArgumentException("Invalid index " + index); 94 | } 95 | } 96 | 97 | @Override 98 | Dictionary expand(String key, Object value) { 99 | return new Dictionary4(this, key, value); 100 | } 101 | 102 | @Override 103 | int contractThreshold() { 104 | return Dictionary2.CAPACITY; 105 | } 106 | 107 | @Override 108 | Dictionary contract() { 109 | return new Dictionary2(this); 110 | } 111 | 112 | @Override 113 | public Dictionary copyOf() { 114 | return new Dictionary3(this); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/dict/Dictionary4.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.dict; 2 | 3 | public class Dictionary4 extends PrimitiveDictionary implements Dictionary { 4 | public static final int CAPACITY = 4; 5 | 6 | String key0; 7 | Object value0; 8 | 9 | String key1; 10 | Object value1; 11 | 12 | String key2; 13 | Object value2; 14 | 15 | String key3; 16 | Object value3; 17 | 18 | // Expand constructor 19 | public Dictionary4(Dictionary3 base, String key, Object value) { 20 | this.key0 = base.key0; 21 | this.value0 = base.value0; 22 | 23 | this.key1 = base.key1; 24 | this.value1 = base.value1; 25 | 26 | this.key2 = base.key2; 27 | this.value2 = base.value2; 28 | 29 | // Last key 30 | this.key3 = key; 31 | this.value3 = value; 32 | } 33 | 34 | // Contract constructor 35 | public Dictionary4(Dictionary6 base) { 36 | this.key0 = base.key0; 37 | this.value0 = base.value0; 38 | 39 | this.key1 = base.key1; 40 | this.value1 = base.value1; 41 | 42 | this.key2 = base.key2; 43 | this.value2 = base.value2; 44 | 45 | this.key3 = base.key3; 46 | this.value3 = base.value3; 47 | } 48 | 49 | // Copy constructor 50 | public Dictionary4(Dictionary4 base) { 51 | this.key0 = base.key0; 52 | this.value0 = base.value0; 53 | 54 | this.key1 = base.key1; 55 | this.value1 = base.value1; 56 | 57 | this.key2 = base.key2; 58 | this.value2 = base.value2; 59 | 60 | this.key3 = base.key3; 61 | this.value3 = base.value3; 62 | } 63 | 64 | // FromMap constructor 65 | public Dictionary4(String[] keys, Object[] values) { 66 | this.key0 = lookupKey(keys, 0); 67 | this.value0 = lookupValue(values, 0); 68 | 69 | this.key1 = lookupKey(keys, 1); 70 | this.value1 = lookupValue(values, 1); 71 | 72 | this.key2 = lookupKey(keys, 2); 73 | this.value2 = lookupValue(values, 2); 74 | 75 | this.key3 = lookupKey(keys, 3); 76 | this.value3 = lookupValue(values, 3); 77 | } 78 | 79 | @Override 80 | String[] keys() { 81 | return new String[] {key0, key1, key2, key3}; 82 | } 83 | 84 | @Override 85 | Object[] values() { 86 | return new Object[] {value0, value1, value2, value3}; 87 | } 88 | 89 | @Override 90 | void write(int index, String key, Object value) { 91 | switch (index) { 92 | case 0: 93 | key0 = key; 94 | value0 = value; 95 | break; 96 | 97 | case 1: 98 | key1 = key; 99 | value1 = value; 100 | break; 101 | 102 | case 2: 103 | key2 = key; 104 | value2 = value; 105 | break; 106 | 107 | case 3: 108 | key3 = key; 109 | value3 = value; 110 | break; 111 | 112 | default: 113 | throw new IllegalArgumentException("Invalid index " + index); 114 | } 115 | } 116 | 117 | @Override 118 | Dictionary expand(String key, Object value) { 119 | return new Dictionary6(this, key, value); 120 | } 121 | 122 | @Override 123 | int contractThreshold() { 124 | return Dictionary3.CAPACITY; 125 | } 126 | 127 | @Override 128 | Dictionary contract() { 129 | return new Dictionary3(this); 130 | } 131 | 132 | @Override 133 | public Dictionary copyOf() { 134 | return new Dictionary4(this); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/dict/Dictionary6.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.dict; 2 | 3 | public class Dictionary6 extends PrimitiveDictionary implements Dictionary { 4 | public static final int CAPACITY = 6; 5 | 6 | String key0; 7 | Object value0; 8 | 9 | String key1; 10 | Object value1; 11 | 12 | String key2; 13 | Object value2; 14 | 15 | String key3; 16 | Object value3; 17 | 18 | String key4; 19 | Object value4; 20 | 21 | String key5; 22 | Object value5; 23 | 24 | // Expand constructor 25 | public Dictionary6(Dictionary4 base, String key, Object value) { 26 | this.key0 = base.key0; 27 | this.value0 = base.value0; 28 | 29 | this.key1 = base.key1; 30 | this.value1 = base.value1; 31 | 32 | this.key2 = base.key2; 33 | this.value2 = base.value2; 34 | 35 | this.key3 = base.key3; 36 | this.value3 = base.value3; 37 | 38 | // Last key 39 | this.key4 = key; 40 | this.value4 = value; 41 | } 42 | 43 | // Contract constructor 44 | public Dictionary6(Dictionary8 base) { 45 | this.key0 = base.key0; 46 | this.value0 = base.value0; 47 | 48 | this.key1 = base.key1; 49 | this.value1 = base.value1; 50 | 51 | this.key2 = base.key2; 52 | this.value2 = base.value2; 53 | 54 | this.key3 = base.key3; 55 | this.value3 = base.value3; 56 | 57 | this.key4 = base.key4; 58 | this.value4 = base.value4; 59 | 60 | this.key5 = base.key5; 61 | this.value5 = base.value5; 62 | } 63 | 64 | // Copy constructor 65 | public Dictionary6(Dictionary6 base) { 66 | this.key0 = base.key0; 67 | this.value0 = base.value0; 68 | 69 | this.key1 = base.key1; 70 | this.value1 = base.value1; 71 | 72 | this.key2 = base.key2; 73 | this.value2 = base.value2; 74 | 75 | this.key3 = base.key3; 76 | this.value3 = base.value3; 77 | 78 | this.key4 = base.key4; 79 | this.value4 = base.value4; 80 | 81 | this.key5 = base.key5; 82 | this.value5 = base.value5; 83 | } 84 | 85 | // FromMap constructor 86 | public Dictionary6(String[] keys, Object[] values) { 87 | this.key0 = lookupKey(keys, 0); 88 | this.value0 = lookupValue(values, 0); 89 | 90 | this.key1 = lookupKey(keys, 1); 91 | this.value1 = lookupValue(values, 1); 92 | 93 | this.key2 = lookupKey(keys, 2); 94 | this.value2 = lookupValue(values, 2); 95 | 96 | this.key3 = lookupKey(keys, 3); 97 | this.value3 = lookupValue(values, 3); 98 | 99 | this.key4 = lookupKey(keys, 4); 100 | this.value4 = lookupValue(values, 4); 101 | 102 | this.key5 = lookupKey(keys, 5); 103 | this.value5 = lookupValue(values, 5); 104 | } 105 | 106 | @Override 107 | String[] keys() { 108 | return new String[] {key0, key1, key2, key3, key4, key5}; 109 | } 110 | 111 | @Override 112 | Object[] values() { 113 | return new Object[] {value0, value1, value2, value3, value4, value5}; 114 | } 115 | 116 | @Override 117 | void write(int index, String key, Object value) { 118 | switch (index) { 119 | case 0: 120 | key0 = key; 121 | value0 = value; 122 | break; 123 | 124 | case 1: 125 | key1 = key; 126 | value1 = value; 127 | break; 128 | 129 | case 2: 130 | key2 = key; 131 | value2 = value; 132 | break; 133 | 134 | case 3: 135 | key3 = key; 136 | value3 = value; 137 | break; 138 | 139 | case 4: 140 | key4 = key; 141 | value4 = value; 142 | break; 143 | 144 | case 5: 145 | key5 = key; 146 | value5 = value; 147 | break; 148 | 149 | default: 150 | throw new IllegalArgumentException("Invalid index " + index); 151 | } 152 | } 153 | 154 | @Override 155 | Dictionary expand(String key, Object value) { 156 | return new Dictionary8(this, key, value); 157 | } 158 | 159 | @Override 160 | int contractThreshold() { 161 | return Dictionary4.CAPACITY; 162 | } 163 | 164 | @Override 165 | Dictionary contract() { 166 | return new Dictionary4(this); 167 | } 168 | 169 | @Override 170 | public Dictionary copyOf() { 171 | return new Dictionary6(this); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/dict/DictionaryFactory.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.dict; 2 | 3 | import java.util.Map; 4 | 5 | public class DictionaryFactory { 6 | public static Dictionary fromMap(Map properties) { 7 | if (properties == null) { 8 | return null; 9 | } 10 | 11 | int size = properties.size(); 12 | String[] keys = new String[size]; 13 | Object[] values = new Object[size]; 14 | 15 | int counter = 0; 16 | for (Map.Entry entry : properties.entrySet()) { 17 | keys[counter] = entry.getKey(); 18 | values[counter] = entry.getValue(); 19 | counter++; 20 | } 21 | 22 | // assert counter == size; 23 | 24 | if (size == 0) { 25 | return null; 26 | } else if (size <= 1) { 27 | return new Dictionary1(keys[0], values[0]); 28 | } else if (size <= 2) { 29 | return new Dictionary2(keys, values); 30 | } else if (size <= 3) { 31 | return new Dictionary3(keys, values); 32 | } else if (size <= 4) { 33 | return new Dictionary4(keys, values); 34 | } else if (size <= 6) { 35 | return new Dictionary6(keys, values); 36 | } else if (size <= 8) { 37 | return new Dictionary8(keys, values); 38 | } else if (size <= 11) { 39 | return new Dictionary11(keys, values); 40 | } else if (size <= 16) { 41 | return new Dictionary16(keys, values); 42 | } else { 43 | return new DictionaryMax(keys, values); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/dict/DictionaryMax.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.dict; 2 | 3 | import java.util.Arrays; 4 | 5 | public class DictionaryMax extends PrimitiveDictionary implements Dictionary { 6 | int capacity; 7 | String[] keys; 8 | Object[] values; 9 | 10 | // Expand constructor 11 | public DictionaryMax(Dictionary16 base, String key, Object value) { 12 | this.capacity = 24; 13 | keys = Arrays.copyOf(base.keys(), capacity); 14 | values = Arrays.copyOf(base.values(), capacity); 15 | 16 | keys[16] = key; 17 | values[16] = value; 18 | } 19 | 20 | // Copy constructor 21 | public DictionaryMax(DictionaryMax base) { 22 | this.capacity = base.capacity; 23 | keys = Arrays.copyOf(base.keys(), capacity); 24 | values = Arrays.copyOf(base.values(), capacity); 25 | } 26 | 27 | // FromMap constructor 28 | public DictionaryMax(String[] keys, Object[] values) { 29 | this.capacity = Math.max(24, keys.length + keys.length / 2); 30 | 31 | this.keys = Arrays.copyOf(keys, capacity); 32 | this.values = Arrays.copyOf(values, capacity); 33 | } 34 | 35 | @Override 36 | String[] keys() { 37 | return keys; 38 | } 39 | 40 | @Override 41 | Object[] values() { 42 | return values; 43 | } 44 | 45 | @Override 46 | void write(int index, String key, Object value) { 47 | keys[index] = key; 48 | values[index] = value; 49 | } 50 | 51 | @Override 52 | Dictionary expand(String key, Object value) { 53 | int newCapacity = capacity + (capacity / 2); 54 | keys = Arrays.copyOf(keys, newCapacity); 55 | values = Arrays.copyOf(values, newCapacity); 56 | 57 | keys[capacity] = key; 58 | values[capacity] = value; 59 | 60 | this.capacity = newCapacity; 61 | 62 | return this; 63 | } 64 | 65 | @Override 66 | int contractThreshold() { 67 | return capacity / 2; 68 | } 69 | 70 | @Override 71 | Dictionary contract() { 72 | if (capacity < 14) { 73 | // Move to Dictionary16 74 | return new Dictionary16(this); 75 | } else { 76 | int newCapacity = capacity * 3 / 4; 77 | keys = Arrays.copyOf(keys, newCapacity); 78 | values = Arrays.copyOf(values, newCapacity); 79 | this.capacity = newCapacity; 80 | 81 | return this; 82 | } 83 | } 84 | 85 | @Override 86 | public Dictionary copyOf() { 87 | return new DictionaryMax(this); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/ArraySet.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * This class uses an array-based set implementation rather than SetMax and 7 | * CompactMultiSetMax classes that implement a hash-based set. Neither 8 | * implementation throws ConcurrentModificationException on reads, but expect 9 | * writes to be serialized. 10 | */ 11 | public class ArraySet implements Set { 12 | int size; 13 | Object[] elements; 14 | 15 | public ArraySet(Object[] elements) { 16 | this(elements, elements.length); 17 | } 18 | 19 | protected ArraySet(Object[] elements, int size) { 20 | this.size = size; 21 | this.elements = new Object[size + size / 2]; 22 | 23 | for (int i = 0; i < size; i++) { 24 | this.elements[i] = (T) elements[i]; 25 | } 26 | } 27 | 28 | @Override 29 | public int size() { 30 | return size; 31 | } 32 | 33 | @Override 34 | public Object[] getElements() { 35 | return Arrays.copyOf(elements, size); 36 | } 37 | 38 | @Override 39 | public Object removeElement(T elem) { 40 | // Go over elements and remove the one 41 | for (int i = 0; i < size; i++) { 42 | if (elem.equals(elements[i])) { 43 | if (i < size - 1) { 44 | elements[i] = elements[size - 1]; 45 | } 46 | 47 | this.size--; 48 | elements[size] = null; 49 | 50 | break; 51 | } 52 | } 53 | 54 | if (size < 16) { 55 | return new Set24(getElements()); 56 | } else if (size < elements.length / 2) { 57 | // Using the constructor that cuts the size -- to avoid two array creations 58 | return new ArraySet(elements, size); 59 | } else { 60 | // Use the same object 61 | return this; 62 | } 63 | } 64 | 65 | @Override 66 | public Set addElement(T elem) { 67 | for (int i = 0; i < size; i++) { 68 | if (elem.equals(elements[i])) { 69 | // Nothing to do 70 | return this; 71 | } 72 | } 73 | 74 | if (size < elements.length) { 75 | elements[size] = elem; 76 | this.size++; 77 | return this; 78 | } else { 79 | Set ans = new ArraySet(elements); 80 | ans.addElement(elem); 81 | return ans; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/ClassifierGetter.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | public interface ClassifierGetter { 4 | public C getClassifier(T obj); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/CompactSet.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | public class CompactSet { 4 | public static int size(Object set) { 5 | if (set == null) { 6 | return 0; 7 | } else if (set instanceof Set) { 8 | return ((Set) set).size(); 9 | } else { 10 | return 1; 11 | } 12 | } 13 | 14 | public static Object[] getElements(Object set) { 15 | if (set == null) { 16 | return new Object[0]; 17 | } else if (set instanceof Set) { 18 | return ((Set) set).getElements(); 19 | } else { 20 | return new Object[] {set}; 21 | } 22 | } 23 | 24 | public static Object add(Object set, T elem) { 25 | if (set == null) { 26 | return elem; 27 | } else if (set instanceof Set) { 28 | return ((Set) set).addElement(elem); 29 | } else { 30 | if (set.equals(elem)) { 31 | return set; 32 | } else { 33 | return new Set2(set, elem); 34 | } 35 | } 36 | } 37 | 38 | public static Object addSafe(Object set, T elem) { 39 | if ((set instanceof Set24) && (CompactSet.size(set) == 24)) { 40 | // Move to ArraySet instead of SetMax to avoid cyclic dependency from CompactMultiSetMax and SetMax 41 | set = new ArraySet(CompactSet.getElements(set)); 42 | } 43 | 44 | return CompactSet.add(set, elem); 45 | } 46 | 47 | public static Object remove(Object set, T elem) { 48 | if (set == null) { 49 | return null; 50 | } else if (set instanceof Set) { 51 | return ((Set) set).removeElement(elem); 52 | } else { 53 | if ((set == elem) || (set.equals(elem))) { 54 | return null; 55 | } else { 56 | return set; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/PrimitiveSet.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | import java.util.Arrays; 4 | 5 | public abstract class PrimitiveSet implements Set { 6 | public PrimitiveSet() { 7 | // Nothing to do 8 | } 9 | 10 | abstract Object[] elements(); 11 | 12 | abstract void write(int index, T elem); 13 | 14 | abstract Set expand(T elem); 15 | 16 | abstract int contractThreshold(); 17 | 18 | abstract Object contract(); 19 | 20 | public int size() { 21 | Object[] elems = elements(); 22 | 23 | return size(elems); 24 | } 25 | 26 | private int size(Object[] elems) { 27 | int i; 28 | for (i = 0; i < elems.length; i++) { 29 | if (elems[i] == null) { 30 | break; 31 | } 32 | } 33 | 34 | return i; 35 | } 36 | 37 | public Object[] getElements() { 38 | Object[] elems = elements(); 39 | 40 | int i; 41 | for (i = 0; i < elems.length; i++) { 42 | if (elems[i] == null) { 43 | break; 44 | } 45 | } 46 | 47 | return Arrays.copyOf(elems, i); 48 | } 49 | 50 | public Set addElement(T elem) { 51 | Object[] elems = elements(); 52 | int size = elems.length; 53 | 54 | boolean duplicate = false; 55 | int i; 56 | for (i = 0; i < size; i++) { 57 | T curElem = (T) elems[i]; 58 | 59 | if (curElem == null) { 60 | // End of keys 61 | break; 62 | } else if (elems[i].equals(elem)) { 63 | duplicate = true; 64 | } 65 | } 66 | 67 | if (duplicate) { 68 | // Stick with this 69 | return this; 70 | } else { 71 | if (i == size) { 72 | // Reached end, need to move up 73 | return expand(elem); 74 | } else { 75 | // Not yet at the end 76 | write(i, elem); 77 | 78 | return this; 79 | } 80 | } 81 | } 82 | 83 | @Override 84 | public Object removeElement(T elem) { 85 | Object[] elems = elements(); 86 | int size = elems.length; 87 | 88 | int overwritePos = -1; 89 | int i; 90 | for (i = 0; i < size; i++) { 91 | Object curElem = elems[i]; 92 | 93 | if (curElem == null) { 94 | // End of keys 95 | break; 96 | } else if (curElem.equals(elem)) { 97 | overwritePos = i; 98 | } 99 | } 100 | 101 | if (overwritePos == -1) { 102 | // Couldn't find key 103 | return this; 104 | } else { 105 | // Overwrite from end to here 106 | int lastIdx = i - 1; 107 | if (overwritePos != lastIdx) { 108 | write(overwritePos, (T) elems[lastIdx]); 109 | } 110 | write(lastIdx, null); 111 | 112 | if (lastIdx <= contractThreshold()) { 113 | // The new size is at or below the contract threshold 114 | return contract(); 115 | } else { 116 | return this; 117 | } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/Set.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | /** A bag keeps an unordered, unstable collection of elements */ 4 | public interface Set { 5 | public int size(); 6 | 7 | public Object[] getElements(); 8 | 9 | public Object removeElement(T elem); 10 | 11 | public Set addElement(T elem); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/Set12.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | public class Set12 extends PrimitiveSet implements Set { 4 | T elem0, elem1, elem2, elem3, elem4, elem5, elem6, elem7, elem8, elem9, elem10, elem11; 5 | 6 | public Set12(Set8 oldSet, T elem) { 7 | this.elem0 = oldSet.elem0; 8 | this.elem1 = oldSet.elem1; 9 | this.elem2 = oldSet.elem2; 10 | this.elem3 = oldSet.elem3; 11 | this.elem4 = oldSet.elem4; 12 | this.elem5 = oldSet.elem5; 13 | this.elem6 = oldSet.elem6; 14 | this.elem7 = oldSet.elem7; 15 | this.elem8 = elem; 16 | } 17 | 18 | public Set12(Set24 oldSet) { 19 | this.elem0 = oldSet.elem0; 20 | this.elem1 = oldSet.elem1; 21 | this.elem2 = oldSet.elem2; 22 | this.elem3 = oldSet.elem3; 23 | this.elem4 = oldSet.elem4; 24 | this.elem5 = oldSet.elem5; 25 | this.elem6 = oldSet.elem6; 26 | this.elem7 = oldSet.elem7; 27 | this.elem8 = oldSet.elem8; 28 | this.elem9 = oldSet.elem9; 29 | this.elem10 = oldSet.elem10; 30 | this.elem11 = oldSet.elem11; 31 | } 32 | 33 | @Override 34 | public Object[] elements() { 35 | return new Object[] {elem0, elem1, elem2, elem3, elem4, elem5, elem6, elem7, elem8, elem9, elem10, elem11}; 36 | } 37 | 38 | protected void write(int index, T elem) { 39 | if (index < 4) { 40 | switch (index) { 41 | case 0: 42 | this.elem0 = elem; 43 | break; 44 | 45 | case 1: 46 | this.elem1 = elem; 47 | break; 48 | 49 | case 2: 50 | this.elem2 = elem; 51 | break; 52 | 53 | case 3: 54 | this.elem3 = elem; 55 | break; 56 | 57 | default: 58 | throw new RuntimeException("Bug in code"); 59 | } 60 | } else if (index < 8) { 61 | switch (index) { 62 | case 4: 63 | this.elem4 = elem; 64 | break; 65 | 66 | case 5: 67 | this.elem5 = elem; 68 | break; 69 | 70 | case 6: 71 | this.elem6 = elem; 72 | break; 73 | 74 | case 7: 75 | this.elem7 = elem; 76 | break; 77 | 78 | default: 79 | throw new IllegalArgumentException("Invalid index " + index); 80 | } 81 | } else { 82 | switch (index) { 83 | case 8: 84 | this.elem8 = elem; 85 | break; 86 | 87 | case 9: 88 | this.elem9 = elem; 89 | break; 90 | 91 | case 10: 92 | this.elem10 = elem; 93 | break; 94 | 95 | case 11: 96 | this.elem11 = elem; 97 | break; 98 | 99 | default: 100 | throw new IllegalArgumentException("Invalid index " + index); 101 | } 102 | } 103 | } 104 | 105 | @Override 106 | Set expand(T elem) { 107 | return new Set24(this, elem); 108 | } 109 | 110 | @Override 111 | int contractThreshold() { 112 | return 8; 113 | } 114 | 115 | @Override 116 | Set contract() { 117 | return new Set8(this); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/Set2.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | public class Set2 extends PrimitiveSet implements Set { 4 | T elem0, elem1; 5 | 6 | public Set2(T elem0, T elem1) { 7 | this.elem0 = elem0; 8 | this.elem1 = elem1; 9 | } 10 | 11 | public Set2(Set3 oldBag) { 12 | this.elem0 = oldBag.elem0; 13 | this.elem1 = oldBag.elem1; 14 | } 15 | 16 | @Override 17 | public Object[] elements() { 18 | return new Object[] {elem0, elem1}; 19 | } 20 | 21 | protected void write(int index, T elem) { 22 | switch (index) { 23 | case 0: 24 | elem0 = elem; 25 | break; 26 | 27 | case 1: 28 | elem1 = elem; 29 | break; 30 | 31 | default: 32 | throw new IllegalArgumentException("Invalid index " + index); 33 | } 34 | } 35 | 36 | @Override 37 | Set expand(T elem) { 38 | return new Set3(this, elem); 39 | } 40 | 41 | @Override 42 | int contractThreshold() { 43 | return 1; 44 | } 45 | 46 | @Override 47 | Object contract() { 48 | return this.elem0; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/Set3.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | public class Set3 extends PrimitiveSet implements Set { 4 | T elem0, elem1, elem2; 5 | 6 | public Set3(Set2 oldSet, T elem) { 7 | this.elem0 = oldSet.elem0; 8 | this.elem1 = oldSet.elem1; 9 | this.elem2 = elem; 10 | } 11 | 12 | public Set3(Set4 oldSet) { 13 | this.elem0 = oldSet.elem0; 14 | this.elem1 = oldSet.elem1; 15 | this.elem2 = oldSet.elem2; 16 | } 17 | 18 | @Override 19 | public Object[] elements() { 20 | return new Object[] {elem0, elem1, elem2}; 21 | } 22 | 23 | protected void write(int index, T elem) { 24 | switch (index) { 25 | case 0: 26 | elem0 = elem; 27 | break; 28 | 29 | case 1: 30 | elem1 = elem; 31 | break; 32 | 33 | case 2: 34 | elem2 = elem; 35 | break; 36 | 37 | default: 38 | throw new IllegalArgumentException("Invalid index " + index); 39 | } 40 | } 41 | 42 | @Override 43 | Set expand(T elem) { 44 | return new Set4(this, elem); 45 | } 46 | 47 | @Override 48 | int contractThreshold() { 49 | return 2; 50 | } 51 | 52 | @Override 53 | Set contract() { 54 | return new Set2(this); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/Set4.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | public class Set4 extends PrimitiveSet implements Set { 4 | T elem0, elem1, elem2, elem3; 5 | 6 | public Set4(Set3 oldSet, T elem) { 7 | this.elem0 = oldSet.elem0; 8 | this.elem1 = oldSet.elem1; 9 | this.elem2 = oldSet.elem2; 10 | this.elem3 = elem; 11 | } 12 | 13 | public Set4(Set6 oldSet) { 14 | this.elem0 = oldSet.elem0; 15 | this.elem1 = oldSet.elem1; 16 | this.elem2 = oldSet.elem2; 17 | this.elem3 = oldSet.elem3; 18 | } 19 | 20 | @Override 21 | public Object[] elements() { 22 | return new Object[] {elem0, elem1, elem2, elem3}; 23 | } 24 | 25 | protected void write(int index, T elem) { 26 | switch (index) { 27 | case 0: 28 | elem0 = elem; 29 | break; 30 | 31 | case 1: 32 | elem1 = elem; 33 | break; 34 | 35 | case 2: 36 | elem2 = elem; 37 | break; 38 | 39 | case 3: 40 | elem3 = elem; 41 | break; 42 | 43 | default: 44 | throw new IllegalArgumentException("Invalid index " + index); 45 | } 46 | } 47 | 48 | @Override 49 | Set expand(T elem) { 50 | return new Set6(this, elem); 51 | } 52 | 53 | @Override 54 | int contractThreshold() { 55 | return 3; 56 | } 57 | 58 | @Override 59 | Set contract() { 60 | return new Set3(this); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/Set6.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | public class Set6 extends PrimitiveSet implements Set { 4 | T elem0, elem1, elem2, elem3, elem4, elem5; 5 | 6 | public Set6(Set4 oldSet, T elem) { 7 | this.elem0 = oldSet.elem0; 8 | this.elem1 = oldSet.elem1; 9 | this.elem2 = oldSet.elem2; 10 | this.elem3 = oldSet.elem3; 11 | this.elem4 = elem; 12 | } 13 | 14 | public Set6(Set8 oldSet) { 15 | this.elem0 = oldSet.elem0; 16 | this.elem1 = oldSet.elem1; 17 | this.elem2 = oldSet.elem2; 18 | this.elem3 = oldSet.elem3; 19 | this.elem4 = oldSet.elem4; 20 | this.elem5 = oldSet.elem5; 21 | } 22 | 23 | @Override 24 | public Object[] elements() { 25 | return new Object[] {elem0, elem1, elem2, elem3, elem4, elem5}; 26 | } 27 | 28 | protected void write(int index, T elem) { 29 | switch (index) { 30 | case 0: 31 | elem0 = elem; 32 | break; 33 | 34 | case 1: 35 | elem1 = elem; 36 | break; 37 | 38 | case 2: 39 | elem2 = elem; 40 | break; 41 | 42 | case 3: 43 | elem3 = elem; 44 | break; 45 | 46 | case 4: 47 | elem4 = elem; 48 | break; 49 | 50 | case 5: 51 | elem5 = elem; 52 | break; 53 | 54 | default: 55 | throw new IllegalArgumentException("Invalid index " + index); 56 | } 57 | } 58 | 59 | @Override 60 | Set expand(T elem) { 61 | return new Set8(this, elem); 62 | } 63 | 64 | @Override 65 | int contractThreshold() { 66 | return 4; 67 | } 68 | 69 | @Override 70 | Set contract() { 71 | return new Set4(this); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/Set8.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | public class Set8 extends PrimitiveSet implements Set { 4 | T elem0, elem1, elem2, elem3, elem4, elem5, elem6, elem7; 5 | 6 | public Set8(Set6 oldSet, T elem) { 7 | this.elem0 = oldSet.elem0; 8 | this.elem1 = oldSet.elem1; 9 | this.elem2 = oldSet.elem2; 10 | this.elem3 = oldSet.elem3; 11 | this.elem4 = oldSet.elem4; 12 | this.elem5 = oldSet.elem5; 13 | this.elem6 = elem; 14 | } 15 | 16 | public Set8(Set12 oldSet) { 17 | this.elem0 = oldSet.elem0; 18 | this.elem1 = oldSet.elem1; 19 | this.elem2 = oldSet.elem2; 20 | this.elem3 = oldSet.elem3; 21 | this.elem4 = oldSet.elem4; 22 | this.elem5 = oldSet.elem5; 23 | this.elem6 = oldSet.elem6; 24 | this.elem7 = oldSet.elem7; 25 | } 26 | 27 | @Override 28 | public Object[] elements() { 29 | return new Object[] {elem0, elem1, elem2, elem3, elem4, elem5, elem6, elem7}; 30 | } 31 | 32 | protected void write(int index, T elem) { 33 | if (index < 4) { 34 | switch (index) { 35 | case 0: 36 | this.elem0 = elem; 37 | break; 38 | 39 | case 1: 40 | this.elem1 = elem; 41 | break; 42 | 43 | case 2: 44 | this.elem2 = elem; 45 | break; 46 | 47 | case 3: 48 | this.elem3 = elem; 49 | break; 50 | 51 | default: 52 | throw new RuntimeException("Bug in code"); 53 | } 54 | } else { 55 | switch (index) { 56 | case 4: 57 | this.elem4 = elem; 58 | break; 59 | 60 | case 5: 61 | this.elem5 = elem; 62 | break; 63 | 64 | case 6: 65 | this.elem6 = elem; 66 | break; 67 | 68 | case 7: 69 | this.elem7 = elem; 70 | break; 71 | 72 | default: 73 | throw new IllegalArgumentException("Invalid index " + index); 74 | } 75 | } 76 | } 77 | 78 | @Override 79 | Set expand(T elem) { 80 | return new Set12(this, elem); 81 | } 82 | 83 | @Override 84 | int contractThreshold() { 85 | return 6; 86 | } 87 | 88 | @Override 89 | Set contract() { 90 | return new Set6(this); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/ads/set/SetMax.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | public class SetMax implements Set { 4 | private static final long serialVersionUID = 129583038274146507L; 5 | 6 | private static final ClassifierGetter identityClassifer = new ClassifierGetter() { 7 | @Override 8 | public Object getClassifier(Object obj) { 9 | return obj; 10 | } 11 | }; 12 | 13 | int size; 14 | CompactMultiSetMax hashSet; 15 | 16 | public SetMax(Set24 oldSet, Object elem) { 17 | // When using a compact multi-set inside a set, it is better to use 18 | // safe-mode to avoid a cyclic dependency when all hash-codes are same 19 | hashSet = new CompactMultiSetMax(32, true); 20 | 21 | Object[] elems = oldSet.getElements(); 22 | for (int i = 0; i < elems.length; i++) { 23 | T curElem = (T) elems[i]; 24 | hashSet.addElementNoRehash(curElem.hashCode(), curElem); 25 | } 26 | 27 | T curElem = (T) elem; 28 | hashSet.addElementNoRehash(curElem.hashCode(), curElem); 29 | 30 | this.size = 1 + elems.length; 31 | 32 | assert (this.size == 25); 33 | } 34 | 35 | @Override 36 | public int size() { 37 | // Expensive operation -- could be inaccurate for lock-free reads 38 | // Doesn't matter because the read will retry 39 | return getElements().length; 40 | } 41 | 42 | @Override 43 | public Object[] getElements() { 44 | return hashSet.getAllElements(); 45 | } 46 | 47 | @Override 48 | public Set removeElement(T elem) { 49 | if (hashSet.elements.length <= 32) { 50 | // Don't resize under 32 -- Better to move to Set24 51 | hashSet.removeElementNoHash(elem.hashCode(), elem); 52 | } else { 53 | // Resize is OK 54 | hashSet = hashSet.remove(elem, identityClassifer); 55 | } 56 | 57 | // The first check increases the chance of the second (more expensive one) succeeding 58 | if ((hashSet.getOccupiedCells() <= 13) && (size() <= 24)) { 59 | // The new size is at or below 24 60 | return new Set24(getElements()); 61 | } else { 62 | return this; 63 | } 64 | } 65 | 66 | @Override 67 | public Set addElement(T elem) { 68 | // Resize is OK on add 69 | hashSet = hashSet.add(elem, identityClassifer); 70 | 71 | return this; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/gremlin/BitsyTraversalStrategy.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.gremlin; 2 | 3 | import org.apache.tinkerpop.gremlin.process.traversal.Step; 4 | import org.apache.tinkerpop.gremlin.process.traversal.Traversal; 5 | import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; 6 | import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder; 7 | import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep; 8 | import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep; 9 | import org.apache.tinkerpop.gremlin.process.traversal.step.map.NoOpBarrierStep; 10 | import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer; 11 | import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy; 12 | import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; 13 | 14 | // Bitsy traversal strategy based Tinkerpop's Neo4j implementation 15 | public class BitsyTraversalStrategy extends AbstractTraversalStrategy 16 | implements TraversalStrategy.ProviderOptimizationStrategy { 17 | private static final long serialVersionUID = 6405194525894707932L; 18 | private static final BitsyTraversalStrategy INSTANCE = new BitsyTraversalStrategy(); 19 | 20 | private BitsyTraversalStrategy() {} 21 | 22 | @Override 23 | public void apply(final Traversal.Admin traversal) { 24 | for (final GraphStep originalGraphStep : TraversalHelper.getStepsOfClass(GraphStep.class, traversal)) { 25 | final BitsyGraphStep bitsyGraphStep = new BitsyGraphStep<>(originalGraphStep); 26 | TraversalHelper.replaceStep(originalGraphStep, bitsyGraphStep, traversal); 27 | Step currentStep = bitsyGraphStep.getNextStep(); 28 | while (currentStep instanceof HasStep || currentStep instanceof NoOpBarrierStep) { 29 | if (currentStep instanceof HasStep) { 30 | for (final HasContainer hasContainer : ((HasContainerHolder) currentStep).getHasContainers()) { 31 | if (!GraphStep.processHasContainerIds(bitsyGraphStep, hasContainer)) 32 | bitsyGraphStep.addHasContainer(hasContainer); 33 | } 34 | TraversalHelper.copyLabels(currentStep, currentStep.getPreviousStep(), false); 35 | traversal.removeStep(currentStep); 36 | } 37 | currentStep = currentStep.getNextStep(); 38 | } 39 | } 40 | } 41 | 42 | public static BitsyTraversalStrategy instance() { 43 | return INSTANCE; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/index/BitsyIndex.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.index; 2 | 3 | import com.lambdazen.bitsy.ads.set.CompactSet; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | public abstract class BitsyIndex { 12 | Map index; 13 | 14 | public BitsyIndex() { 15 | this.index = new ConcurrentHashMap(); 16 | } 17 | 18 | public abstract Object getValue(T bean); 19 | 20 | public abstract T copy(T bean); 21 | 22 | public void load(Iterator initialContents) { 23 | while (initialContents.hasNext()) { 24 | T elem = initialContents.next(); 25 | add(elem); 26 | } 27 | } 28 | 29 | public List get(Object value) { 30 | Object idxValue = index.get(value); 31 | 32 | if (idxValue == null) { 33 | return Collections.emptyList(); 34 | } else { 35 | Object[] objs = CompactSet.getElements(idxValue); 36 | List ans = new ArrayList(objs.length); 37 | int len = objs.length; 38 | for (int i = 0; i < len; i++) { 39 | // Always check for nulls on getElements() because reads don't acquire locks 40 | if (objs[i] != null) { 41 | ans.add(copy((T) objs[i])); 42 | } 43 | } 44 | 45 | return ans; 46 | } 47 | } 48 | 49 | public void add(T bean) { 50 | Object value = getValue(bean); 51 | if (value == null) { 52 | // Nothing to do 53 | return; 54 | } 55 | 56 | // No need to synchronize, because there is a read-write lock 57 | Object origSet = index.get(value); 58 | Object newSet = CompactSet.add(origSet, bean); 59 | 60 | if (origSet != newSet) { 61 | index.put(value, newSet); 62 | } 63 | } 64 | 65 | public void remove(T bean) { 66 | Object value = getValue(bean); 67 | if (value == null) { 68 | // Nothing to do 69 | return; 70 | } 71 | 72 | // No need to synchronize, because there is a read-write lock 73 | Object origSet = index.get(value); 74 | Object newSet = CompactSet.remove(origSet, bean); 75 | 76 | if (origSet != newSet) { 77 | if (newSet == null) { 78 | index.remove(value); 79 | } else { 80 | index.put(value, newSet); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/index/BitsyIndexMap.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.index; 2 | 3 | import com.lambdazen.bitsy.BitsyErrorCodes; 4 | import com.lambdazen.bitsy.BitsyException; 5 | import java.util.Collection; 6 | import java.util.HashSet; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | public class BitsyIndexMap> { 12 | // Read operations on IndexMap don't require locks. 13 | Map indexMap; 14 | 15 | public BitsyIndexMap() { 16 | this.indexMap = new ConcurrentHashMap(); 17 | } 18 | 19 | // LOCK-FREE methods: The member indexNames can not be used. 20 | /** This method returns a copy of the edges/vertices held for the given key and value */ 21 | public Collection get(String key, Object value) { 22 | IndexType index = indexMap.get(key); 23 | 24 | if (index == null) { 25 | throw new BitsyException( 26 | BitsyErrorCodes.MISSING_INDEX, 27 | "An index on " + key 28 | + " must be created before querying vertices/edges by that key. Defined indexes: " 29 | + indexMap.keySet()); 30 | } else { 31 | return index.get(value); 32 | } 33 | } 34 | 35 | // LOCKED methods 36 | public void add(BeanType bean) { 37 | for (IndexType index : indexMap.values()) { 38 | index.add(bean); 39 | } 40 | } 41 | 42 | public void remove(BeanType bean) { 43 | if (bean == null) { 44 | // Nothing to do 45 | return; 46 | } 47 | 48 | for (IndexType index : indexMap.values()) { 49 | index.remove(bean); 50 | } 51 | } 52 | 53 | protected void addKeyIndex(String key, IndexType index) { 54 | if (indexMap.containsKey(key)) { 55 | throw new BitsyException(BitsyErrorCodes.INDEX_ALREADY_EXISTS, "Index on vertex key '" + key + "'"); 56 | } 57 | 58 | indexMap.put(key, index); 59 | } 60 | 61 | public void dropKeyIndex(String key) { 62 | indexMap.remove(key); 63 | } 64 | 65 | public Set getIndexedKeys() { 66 | return new HashSet(indexMap.keySet()); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/index/EdgeIndex.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.index; 2 | 3 | import com.lambdazen.bitsy.ads.dict.Dictionary; 4 | import com.lambdazen.bitsy.store.EdgeBean; 5 | import java.util.Iterator; 6 | 7 | public class EdgeIndex extends BitsyIndex { 8 | String key; 9 | 10 | public EdgeIndex(String key, Iterator initialContents) { 11 | super(); 12 | 13 | this.key = key; 14 | 15 | load(initialContents); 16 | } 17 | 18 | @Override 19 | public Object getValue(EdgeBean bean) { 20 | Dictionary props = bean.getPropertiesDict(); 21 | return (props == null) ? null : props.getProperty(key); 22 | } 23 | 24 | @Override 25 | public EdgeBean copy(EdgeBean bean) { 26 | return new EdgeBean(bean); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/index/EdgeIndexMap.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.index; 2 | 3 | import com.lambdazen.bitsy.store.EdgeBean; 4 | import java.util.Iterator; 5 | 6 | public class EdgeIndexMap extends BitsyIndexMap { 7 | public EdgeIndexMap() { 8 | super(); 9 | } 10 | 11 | public void createKeyIndex(String key, Iterator iter) { 12 | EdgeIndex index = new EdgeIndex(key, iter); 13 | addKeyIndex(key, index); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/index/IndexHelper.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.index; 2 | 3 | import com.lambdazen.bitsy.BitsyElement; 4 | import com.lambdazen.bitsy.BitsyState; 5 | import com.lambdazen.bitsy.store.EdgeBean; 6 | import com.lambdazen.bitsy.store.VertexBean; 7 | import java.util.ArrayList; 8 | import java.util.Collection; 9 | 10 | public class IndexHelper { 11 | public static Collection filterElementsByKeyValue( 12 | Collection elems, String key, Object value) { 13 | ArrayList ans = new ArrayList(); 14 | 15 | for (T elem : elems) { 16 | if (elem.getState() == BitsyState.D) { 17 | continue; 18 | } 19 | 20 | Object elemVal = elem.value(key); 21 | 22 | if ((elemVal != null) && (elemVal.equals(value))) { 23 | ans.add(elem); 24 | } 25 | } 26 | 27 | return ans; 28 | } 29 | 30 | public static Collection filterVertexBeansByKeyValue( 31 | Collection elems, String key, Object value) { 32 | ArrayList ans = new ArrayList(); 33 | 34 | for (VertexBean elem : elems) { 35 | Object elemVal = (elem.getProperties() == null) 36 | ? null 37 | : (elem.getProperties().get(key)); 38 | 39 | if ((elemVal != null) && (elemVal.equals(value))) { 40 | ans.add(elem); 41 | } 42 | } 43 | 44 | return ans; 45 | } 46 | 47 | public static Collection filterEdgeBeansByKeyValue(Collection elems, String key, Object value) { 48 | ArrayList ans = new ArrayList(); 49 | 50 | for (EdgeBean elem : elems) { 51 | Object elemVal = (elem.getPropertiesDict() == null) 52 | ? null 53 | : (elem.getPropertiesDict().getProperty(key)); 54 | 55 | if ((elemVal != null) && (elemVal.equals(value))) { 56 | ans.add(elem); 57 | } 58 | } 59 | 60 | return ans; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/index/VertexIndex.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.index; 2 | 3 | import com.lambdazen.bitsy.store.VertexBean; 4 | import java.util.Iterator; 5 | import java.util.Map; 6 | 7 | public class VertexIndex extends BitsyIndex { 8 | String key; 9 | 10 | public VertexIndex(String key, Iterator initialContents) { 11 | super(); 12 | 13 | this.key = key; 14 | 15 | load(initialContents); 16 | } 17 | 18 | @Override 19 | public Object getValue(VertexBean bean) { 20 | Map props = bean.getProperties(); 21 | return (props == null) ? null : props.get(key); 22 | } 23 | 24 | @Override 25 | public VertexBean copy(VertexBean bean) { 26 | return new VertexBean(bean); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/index/VertexIndexMap.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.index; 2 | 3 | import com.lambdazen.bitsy.store.VertexBean; 4 | import java.util.Iterator; 5 | 6 | public class VertexIndexMap extends BitsyIndexMap { 7 | public VertexIndexMap() { 8 | super(); 9 | } 10 | 11 | public void createKeyIndex(String key, Iterator iter) { 12 | VertexIndex index = new VertexIndex(key, iter); 13 | addKeyIndex(key, index); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/jsr223/BitsyGremlinPlugin.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.jsr223; 2 | 3 | import com.lambdazen.bitsy.BitsyEdge; 4 | import com.lambdazen.bitsy.BitsyElement; 5 | import com.lambdazen.bitsy.BitsyGraph; 6 | import com.lambdazen.bitsy.BitsyIoRegistryV3d0; 7 | import com.lambdazen.bitsy.BitsyProperty; 8 | import com.lambdazen.bitsy.BitsyVertex; 9 | import com.lambdazen.bitsy.BitsyVertexProperty; 10 | import com.lambdazen.bitsy.ThreadedBitsyGraph; 11 | import com.lambdazen.bitsy.UUID; 12 | import com.lambdazen.bitsy.tx.BitsyTransaction; 13 | import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinPlugin; 14 | import org.apache.tinkerpop.gremlin.jsr223.DefaultImportCustomizer; 15 | import org.apache.tinkerpop.gremlin.jsr223.ImportCustomizer; 16 | 17 | public class BitsyGremlinPlugin extends AbstractGremlinPlugin { 18 | private static final String NAME = "lambdazen.bitsy"; 19 | 20 | private static ImportCustomizer imports() { 21 | return DefaultImportCustomizer.build() 22 | .addClassImports( 23 | BitsyEdge.class, 24 | BitsyElement.class, 25 | BitsyGraph.class, 26 | ThreadedBitsyGraph.class, 27 | BitsyProperty.class, 28 | BitsyVertex.class, 29 | BitsyVertexProperty.class, 30 | UUID.class, 31 | BitsyTransaction.class, 32 | BitsyIoRegistryV3d0.class) 33 | .create(); 34 | } 35 | 36 | private static final BitsyGremlinPlugin INSTANCE = new BitsyGremlinPlugin(); 37 | 38 | public BitsyGremlinPlugin() { 39 | super(NAME, imports()); 40 | } 41 | 42 | public static BitsyGremlinPlugin instance() { 43 | return INSTANCE; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/BackupJob.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.lambdazen.bitsy.BitsyException; 4 | import java.nio.file.Path; 5 | 6 | public class BackupJob extends JobWithCountDownLatch implements IVeReorgJob { 7 | private Path backupDir; 8 | private BitsyException bex; 9 | 10 | public BackupJob(Path backupDir) { 11 | this.backupDir = backupDir; 12 | } 13 | 14 | public Path getBackupDir() { 15 | return backupDir; 16 | } 17 | 18 | public BitsyException getException() { 19 | return bex; 20 | } 21 | 22 | public void setException(BitsyException bex) { 23 | this.bex = bex; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/EdgeBean.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 6 | import com.lambdazen.bitsy.IEdge; 7 | import com.lambdazen.bitsy.UUID; 8 | import com.lambdazen.bitsy.ads.dict.Dictionary; 9 | import java.io.Serializable; 10 | import java.util.TreeMap; 11 | 12 | @JsonPropertyOrder({"class", "id", "v", "s", "o", "l", "i", "p"}) 13 | public class EdgeBean extends UUID implements IEdge, Serializable { 14 | private static final long serialVersionUID = -5962479601393604124L; 15 | 16 | Dictionary properties; 17 | String label; 18 | VertexBean outVertex; 19 | VertexBean inVertex; 20 | int version; 21 | 22 | public EdgeBean( 23 | UUID id, Dictionary properties, int version, String label, VertexBean outVertex, VertexBean inVertex) { 24 | super(id.getMostSignificantBits(), id.getLeastSignificantBits()); 25 | 26 | this.properties = properties; 27 | this.version = version; 28 | this.label = label; 29 | this.outVertex = outVertex; 30 | this.inVertex = inVertex; 31 | } 32 | 33 | /** Shallow copy constructor */ 34 | public EdgeBean(EdgeBean orig) { 35 | super(orig.getMostSignificantBits(), orig.getLeastSignificantBits()); 36 | 37 | this.version = orig.version; 38 | this.properties = orig.properties; 39 | this.label = orig.label; 40 | this.outVertex = orig.outVertex; 41 | this.inVertex = orig.inVertex; 42 | } 43 | 44 | // Use UUID's toString() 45 | // public String toString() { 46 | // return "EdgeBean(id = " + getIdStr() + ", props " + getProperties() + ", version = " + version + ", label 47 | // = " + label + ", outV = " + outVertex + ", inV = " + inVertex + ")"; 48 | // } 49 | 50 | @JsonIgnore 51 | public UUID getId() { 52 | // I am the ID! Saves on object creation and equals checks. 53 | return this; 54 | } 55 | 56 | @JsonProperty("id") 57 | public String getIdStr() { 58 | return uuidRepr(); 59 | } 60 | 61 | @JsonProperty("p") 62 | public TreeMap getProperties() { 63 | if (properties == null) { 64 | return null; 65 | } else { 66 | TreeMap ans = new TreeMap(); 67 | 68 | for (String key : properties.getPropertyKeys()) { 69 | ans.put(key, properties.getProperty(key)); 70 | } 71 | 72 | return ans; 73 | } 74 | } 75 | 76 | @JsonIgnore 77 | public Dictionary getPropertiesDict() { 78 | return properties; 79 | } 80 | 81 | @JsonProperty("v") 82 | public int getVersion() { 83 | return version; 84 | } 85 | 86 | @JsonIgnore 87 | public UUID getInVertexId() { 88 | return inVertex.getId(); 89 | } 90 | 91 | @JsonIgnore 92 | public UUID getOutVertexId() { 93 | return outVertex.getId(); 94 | } 95 | 96 | @JsonProperty("l") 97 | public String getLabel() { 98 | return label; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/EdgeBeanJson.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.lambdazen.bitsy.BitsyState; 7 | import com.lambdazen.bitsy.UUID; 8 | import com.lambdazen.bitsy.ads.dict.Dictionary; 9 | import com.lambdazen.bitsy.ads.dict.DictionaryFactory; 10 | import java.util.TreeMap; 11 | 12 | public class EdgeBeanJson extends EdgeBean { 13 | private static final long serialVersionUID = 3554788509798848887L; 14 | 15 | private UUID outVertexId, inVertexId; 16 | private BitsyState state; 17 | 18 | @JsonCreator 19 | public EdgeBeanJson( 20 | @JsonProperty("id") String idStr, 21 | @JsonProperty("p") TreeMap properties, 22 | @JsonProperty("v") int version, 23 | @JsonProperty("l") String label, 24 | @JsonProperty("o") String outVertexIdStr, 25 | @JsonProperty("i") String inVertexIdStr, 26 | @JsonProperty("s") BitsyState state) { 27 | this( 28 | UUID.fromString(idStr), 29 | DictionaryFactory.fromMap(properties), 30 | version, 31 | label, 32 | UUID.fromString(outVertexIdStr), 33 | UUID.fromString(inVertexIdStr), 34 | state); 35 | } 36 | 37 | public EdgeBeanJson( 38 | UUID id, 39 | Dictionary properties, 40 | int version, 41 | String label, 42 | UUID outVertexId, 43 | UUID inVertexId, 44 | BitsyState state) { 45 | super(id, properties, version, label, null, null); 46 | 47 | this.outVertexId = outVertexId; 48 | this.inVertexId = inVertexId; 49 | this.state = state; 50 | } 51 | 52 | @JsonIgnore 53 | @Override 54 | public UUID getInVertexId() { 55 | return inVertexId; 56 | } 57 | 58 | @JsonIgnore 59 | @Override 60 | public UUID getOutVertexId() { 61 | return outVertexId; 62 | } 63 | 64 | @JsonProperty("i") 65 | public String getInVertexIdStr() { 66 | return UUID.toString(inVertexId); 67 | } 68 | 69 | @JsonProperty("o") 70 | public String getOutVertexIdStr() { 71 | return UUID.toString(outVertexId); 72 | } 73 | 74 | @JsonProperty("s") 75 | public BitsyState getState() { 76 | return state; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/Endpoint.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.lambdazen.bitsy.UUID; 4 | import java.io.Serializable; 5 | 6 | /** This class represents an end-point of an edge */ 7 | public class Endpoint implements Serializable, Comparable { 8 | private static final long serialVersionUID = 3826290623768933884L; 9 | 10 | String edgeLabel; 11 | UUID edgeId; 12 | 13 | transient boolean marker; 14 | 15 | public Endpoint(String edgeLabel, UUID edgeId) { 16 | this.edgeLabel = edgeLabel; 17 | this.edgeId = edgeId; 18 | this.marker = false; 19 | } 20 | 21 | public String toString() { 22 | return "Endpoint(label = " + edgeLabel + ", edgeId = " + edgeId + ", marker = " + marker + ")"; 23 | } 24 | 25 | public void setMarker() { 26 | marker = true; 27 | } 28 | 29 | public boolean isMarker() { 30 | return marker; 31 | } 32 | 33 | public String getEdgeLabel() { 34 | return edgeLabel; 35 | } 36 | 37 | public UUID getEdgeId() { 38 | return edgeId; 39 | } 40 | 41 | public int compareTo(Endpoint other) { 42 | int ans = 0; 43 | 44 | // A null edgeLabel implies the 'first' edge for the given vertex 45 | if (edgeLabel == null) { 46 | if (isMarker()) { 47 | return -1; 48 | } 49 | 50 | if (other.getEdgeLabel() != null) { 51 | return -1; 52 | } 53 | } 54 | 55 | // A null on the other implies that the other edge is first 56 | if (other.getEdgeLabel() == null) { 57 | if (other.isMarker()) { 58 | return 1; 59 | } 60 | 61 | if (edgeLabel != null) { 62 | return 1; 63 | } else { 64 | // Continue down to edge ID comparison 65 | } 66 | } else { 67 | // Comes here if both edge labels are non null 68 | ans = edgeLabel.compareTo(other.getEdgeLabel()); 69 | if (ans != 0) { 70 | return ans; 71 | } 72 | 73 | // Continue down to edge ID comparison 74 | } 75 | 76 | // Both vertex and edge labels match. 77 | if (edgeId == null) { 78 | assert isMarker(); 79 | return -1; 80 | } 81 | 82 | if (other.getEdgeId() == null) { 83 | assert other.isMarker(); 84 | return 1; 85 | } 86 | 87 | ans = edgeId.compareTo(other.getEdgeId()); 88 | if (ans != 0) { 89 | return ans; 90 | } 91 | 92 | if (isMarker()) { 93 | return -1; 94 | } else if (other.isMarker()) { 95 | return 1; 96 | } else { 97 | return 0; 98 | } 99 | } 100 | 101 | public boolean equals(Object o) { 102 | if (o instanceof Endpoint) { 103 | return compareTo((Endpoint) o) == 0; 104 | } else { 105 | return false; 106 | } 107 | } 108 | 109 | /** 110 | * This method must only be called on marker endpoints, i.e., end-points 111 | * used for matching existing Endpoints in the B-Tree. It returns true if 112 | * the given Endpoint matches this marker 113 | */ 114 | public boolean isMatch(Endpoint other) { 115 | assert isMarker() : "isMatch can only be used on marker end-points"; 116 | 117 | // Same structure as compareTo, except that nulls 'match' IDs (not <) 118 | int ans = 0; 119 | 120 | // A null edgeLabel implies that all endpoints of that vertex must be 121 | // matched 122 | if (edgeLabel == null) { 123 | return true; 124 | } 125 | 126 | if (other.getEdgeLabel() == null) { 127 | // This is not a match because the existing Endpoint no label, but 128 | // the marker does 129 | return false; 130 | } 131 | 132 | ans = edgeLabel.compareTo(other.getEdgeLabel()); 133 | if (ans != 0) { 134 | return false; 135 | } 136 | 137 | // A null edgeId implies the 'first' edge for the given vertex for the 138 | // given edgeLabel 139 | if (edgeId == null) { 140 | return true; 141 | } 142 | 143 | return (edgeId.compareTo(other.getEdgeId()) == 0); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/IEdgeRemover.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.lambdazen.bitsy.IEdge; 4 | import com.lambdazen.bitsy.UUID; 5 | 6 | public interface IEdgeRemover { 7 | public IEdge removeEdge(UUID id); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/IStringCanonicalizer.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | public interface IStringCanonicalizer { 4 | public String canonicalize(String str); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/ITxBatchJob.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | public interface ITxBatchJob {} 4 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/IVeReorgJob.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | public interface IVeReorgJob {} 4 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/IndexBean.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 7 | import com.lambdazen.bitsy.BitsyErrorCodes; 8 | import com.lambdazen.bitsy.BitsyException; 9 | import org.apache.tinkerpop.gremlin.structure.Edge; 10 | import org.apache.tinkerpop.gremlin.structure.Vertex; 11 | 12 | @JsonPropertyOrder({"type", "key"}) 13 | public class IndexBean { 14 | int type; 15 | String key; 16 | 17 | @JsonCreator 18 | public IndexBean(@JsonProperty("type") int type, @JsonProperty("key") String key) { 19 | this.type = type; 20 | this.key = key; 21 | } 22 | 23 | @JsonProperty("type") 24 | public int getType() { 25 | return type; 26 | } 27 | 28 | @JsonProperty("key") 29 | public String getKey() { 30 | return key; 31 | } 32 | 33 | @JsonIgnore 34 | public Class getIndexClass() { 35 | if (type == 0) { 36 | return Vertex.class; 37 | } else if (type == 1) { 38 | return Edge.class; 39 | } else { 40 | throw new BitsyException(BitsyErrorCodes.DATABASE_IS_CORRUPT, "Unrecognized index type " + type); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/JobWithCountDownLatch.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | 5 | public class JobWithCountDownLatch { 6 | CountDownLatch cdl; 7 | 8 | public JobWithCountDownLatch() { 9 | this.cdl = new CountDownLatch(1); 10 | } 11 | 12 | public CountDownLatch getCountDownLatch() { 13 | return cdl; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/RecordReader.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.fasterxml.jackson.databind.ObjectReader; 4 | import com.lambdazen.bitsy.util.CommittableFileLog; 5 | 6 | public class RecordReader { 7 | CommittableFileLog cfl; 8 | String fileName; 9 | int lineNo = 1; 10 | 11 | ObjectReader vReader; 12 | ObjectReader eReader; 13 | 14 | public RecordReader(CommittableFileLog cfl, ObjectReader vReader, ObjectReader eReader) { 15 | this.cfl = cfl; 16 | this.fileName = cfl.getPath().toString(); 17 | this.vReader = vReader; 18 | this.eReader = eReader; 19 | } 20 | 21 | public Record next() throws Exception { 22 | String line = cfl.readLine(); 23 | 24 | if (line == null) { 25 | return null; 26 | } else { 27 | lineNo++; 28 | 29 | Record ans = Record.parseRecord(line, lineNo, fileName); 30 | ans.deserialize(vReader, eReader); 31 | return ans; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/SingleThreadedStringCanonicalizer.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /* This class is not thread-safe */ 7 | public class SingleThreadedStringCanonicalizer implements IStringCanonicalizer { 8 | Map canonicalStrings; 9 | 10 | public SingleThreadedStringCanonicalizer() { 11 | canonicalStrings = new HashMap(); 12 | } 13 | 14 | public String canonicalize(String str) { 15 | String canonicalString = canonicalStrings.get(str); 16 | if (canonicalString != null) { 17 | return canonicalString; 18 | } else { 19 | canonicalStrings.put(str, str); 20 | 21 | return str; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/TxBatch.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * This class captures a list of transactions that must be written to the 7 | * VA/B and EA/B text files 8 | */ 9 | public class TxBatch implements ITxBatchJob { 10 | List trans; 11 | int size; 12 | 13 | public TxBatch(List trans) { 14 | this.trans = trans; 15 | this.size = 0; 16 | } 17 | 18 | public List getTxUnitList() { 19 | return trans; 20 | } 21 | 22 | public int getSize() { 23 | return size; 24 | } 25 | 26 | public void setSize(int size) { 27 | this.size = size; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/TxLog.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.lambdazen.bitsy.util.CommittableFileLog; 4 | 5 | /** 6 | * This class captures a transaction log that needs to be merged into the VA/B and EA/B text files 7 | */ 8 | public class TxLog implements IVeReorgJob { 9 | int rpd; 10 | CommittableFileLog cfl; 11 | 12 | public TxLog(CommittableFileLog cfl) { 13 | this.cfl = cfl; 14 | this.rpd = 0; 15 | } 16 | 17 | public CommittableFileLog getCommittableFileLog() { 18 | return cfl; 19 | } 20 | 21 | public void setReorgPotDiff(int rpd) { 22 | this.rpd = rpd; 23 | } 24 | 25 | public int getReorgPotDiff() { 26 | return rpd; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/TxLogFlushPotential.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.lambdazen.bitsy.store.FileBackedMemoryGraphStore.FlushNowJob; 4 | import com.lambdazen.bitsy.util.BufferPotential; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * This potential function keeps track of the total bytes written to TA or 10 | * TB.txt and suggests a flush operation when that number exceeds the given 11 | * txLogThreshold 12 | */ 13 | public class TxLogFlushPotential implements BufferPotential { 14 | private static final Logger log = LoggerFactory.getLogger(TxLogFlushPotential.class); 15 | 16 | long txLogThreshold; 17 | long curBufSize; 18 | 19 | public TxLogFlushPotential(long txLogThreshold) { 20 | this.txLogThreshold = txLogThreshold; 21 | this.curBufSize = 0; 22 | } 23 | 24 | public long getTxLogThreshold() { 25 | return txLogThreshold; 26 | } 27 | 28 | public void setTxLogThreshold(long txLogThreshold) { 29 | this.txLogThreshold = txLogThreshold; 30 | } 31 | 32 | @Override 33 | public boolean addWork(ITxBatchJob newWork) { 34 | if (newWork instanceof TxBatch) { 35 | curBufSize += ((TxBatch) newWork).getSize(); 36 | 37 | return (curBufSize > txLogThreshold); 38 | } else if (newWork instanceof FlushNowJob) { 39 | log.debug("Tx buffer has been flushed explicitly"); 40 | 41 | return true; 42 | } else { 43 | // Error has already been logged before 44 | log.debug("Unsupported type of work in TxLogFlushPotential: {}", newWork.getClass()); 45 | 46 | return false; 47 | } 48 | } 49 | 50 | @Override 51 | public void reset() { 52 | curBufSize = 0; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/TxUnit.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.lambdazen.bitsy.BitsyException; 4 | import com.lambdazen.bitsy.util.CommittableFileLog; 5 | import java.nio.ByteBuffer; 6 | 7 | /** This class captures a transaction to be written to the TA/B transaction files */ 8 | public class TxUnit extends JobWithCountDownLatch { 9 | ByteBuffer vertices; 10 | ByteBuffer edges; 11 | ByteBuffer tx; 12 | BitsyException bex; 13 | 14 | public TxUnit(ByteBuffer vertices, ByteBuffer edges, ByteBuffer tx) { 15 | this.vertices = vertices; 16 | this.edges = edges; 17 | this.tx = tx; 18 | } 19 | 20 | public ByteBuffer getByteBufferForV() { 21 | vertices.position(0); 22 | 23 | return vertices; 24 | } 25 | 26 | public ByteBuffer getByteBufferForE() { 27 | edges.position(0); 28 | 29 | return edges; 30 | } 31 | 32 | public ByteBuffer getByteBufferForT() { 33 | tx.position(0); 34 | 35 | return tx; 36 | } 37 | 38 | public int writeToFile(CommittableFileLog cfl) { 39 | ByteBuffer vbb = getByteBufferForV(); 40 | ByteBuffer ebb = getByteBufferForE(); 41 | ByteBuffer tbb = getByteBufferForT(); 42 | 43 | int size = vbb.remaining() + ebb.remaining() + tbb.remaining(); 44 | 45 | cfl.append(vbb); 46 | cfl.append(ebb); 47 | cfl.append(tbb); 48 | 49 | return size; 50 | } 51 | 52 | public BitsyException getException() { 53 | return bex; 54 | } 55 | 56 | public void setException(BitsyException bex) { 57 | this.bex = bex; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/VEObsolescencePotential.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.lambdazen.bitsy.util.BufferPotential; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | public class VEObsolescencePotential implements BufferPotential { 8 | private static final Logger log = LoggerFactory.getLogger(VEObsolescencePotential.class); 9 | 10 | double factor; 11 | long origLines; 12 | long addedLines; 13 | int minLinesPerReorg; 14 | 15 | public VEObsolescencePotential(int minLinesPerReorg, double factor, long origLines) { 16 | this.factor = factor; 17 | this.origLines = origLines; 18 | this.addedLines = 0; 19 | this.minLinesPerReorg = minLinesPerReorg; 20 | } 21 | 22 | @Override 23 | public boolean addWork(IVeReorgJob newWork) { 24 | if (newWork instanceof TxLog) { 25 | this.addedLines += ((TxLog) newWork).getReorgPotDiff(); 26 | 27 | double factorTimesOrigLines = factor * origLines; 28 | 29 | log.debug( 30 | "VE obsolescence potential: {}. Threshold is maximum of {} and {}", 31 | addedLines, 32 | factorTimesOrigLines, 33 | minLinesPerReorg); 34 | 35 | return (addedLines > factorTimesOrigLines) && (addedLines > minLinesPerReorg); 36 | } else { 37 | // Don't reorg 38 | return false; 39 | } 40 | } 41 | 42 | @Override 43 | public void reset() { 44 | addedLines = 0; 45 | } 46 | 47 | // This method is called by the flusher 48 | public void setOrigLines(int origLines) { 49 | this.origLines = origLines; 50 | } 51 | 52 | public double getFactor() { 53 | return factor; 54 | } 55 | 56 | public void setFactor(double factor) { 57 | this.factor = factor; 58 | } 59 | 60 | public int getMinLinesPerReorg() { 61 | return minLinesPerReorg; 62 | } 63 | 64 | public void setMinLinesPerReorg(int minLinesPerReorg) { 65 | this.minLinesPerReorg = minLinesPerReorg; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/VertexBean.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.lambdazen.bitsy.UUID; 6 | import com.lambdazen.bitsy.ads.dict.Dictionary; 7 | import java.io.Serializable; 8 | import java.util.TreeMap; 9 | 10 | public class VertexBean extends UUID implements Serializable { 11 | private static final long serialVersionUID = -2867517568410927192L; 12 | 13 | int version; 14 | String label; 15 | Dictionary properties; 16 | 17 | Object outEdges; 18 | Object inEdges; 19 | 20 | public VertexBean(UUID uuid, String label, Dictionary properties, int version) { 21 | super(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()); 22 | 23 | this.label = label; 24 | this.properties = properties; 25 | this.version = version; 26 | } 27 | 28 | /** Shallow copy constructor */ 29 | public VertexBean(VertexBean orig) { 30 | super(orig.getMostSignificantBits(), orig.getLeastSignificantBits()); 31 | 32 | this.label = orig.label; 33 | this.version = orig.version; 34 | this.properties = orig.properties; 35 | this.outEdges = orig.outEdges; 36 | this.inEdges = orig.inEdges; 37 | } 38 | 39 | @JsonIgnore 40 | public UUID getId() { 41 | // I am the ID! Saves on object creation and equals checks. 42 | return this; 43 | } 44 | 45 | @JsonProperty("id") 46 | public String getIdStr() { 47 | return uuidRepr(); 48 | } 49 | 50 | @JsonProperty("p") 51 | public TreeMap getProperties() { 52 | if (properties == null) { 53 | return null; 54 | } else { 55 | TreeMap ans = new TreeMap(); 56 | 57 | for (String key : properties.getPropertyKeys()) { 58 | ans.put(key, properties.getProperty(key)); 59 | } 60 | 61 | return ans; 62 | } 63 | } 64 | 65 | @JsonIgnore 66 | public Dictionary getPropertiesDict() { 67 | return properties; 68 | } 69 | 70 | @JsonProperty("v") 71 | public int getVersion() { 72 | return version; 73 | } 74 | 75 | @JsonProperty("l") 76 | public String getLabel() { 77 | return label; 78 | } 79 | 80 | public void copyFrom(VertexBean vBean) { 81 | this.version = vBean.version; 82 | this.properties = vBean.properties; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/store/VertexBeanJson.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 6 | import com.lambdazen.bitsy.BitsyState; 7 | import com.lambdazen.bitsy.UUID; 8 | import com.lambdazen.bitsy.ads.dict.Dictionary; 9 | import com.lambdazen.bitsy.ads.dict.DictionaryFactory; 10 | import java.io.Serializable; 11 | import java.util.TreeMap; 12 | 13 | @JsonPropertyOrder({"id", "v", "s", "p"}) 14 | public final class VertexBeanJson extends VertexBean implements Serializable { 15 | private static final long serialVersionUID = 8270238605124987367L; 16 | 17 | BitsyState state; 18 | 19 | @JsonCreator 20 | public VertexBeanJson( 21 | @JsonProperty("id") String uuidStr, 22 | @JsonProperty("l") String label, 23 | @JsonProperty("p") TreeMap properties, 24 | @JsonProperty("v") int version, 25 | @JsonProperty("s") BitsyState state) { 26 | super(UUID.fromString(uuidStr), label, DictionaryFactory.fromMap(properties), version); 27 | 28 | this.state = state; 29 | } 30 | 31 | public VertexBeanJson(UUID id, String label, Dictionary properties, int version, BitsyState state) { 32 | super(id, label, properties, version); 33 | 34 | this.state = state; 35 | } 36 | 37 | @JsonProperty("s") 38 | public BitsyState getState() { 39 | return state; 40 | } 41 | 42 | @JsonProperty("id") 43 | public String getIdStr() { 44 | return getId().uuidRepr(); 45 | } 46 | 47 | @JsonProperty("p") 48 | public TreeMap getProperties() { 49 | if (properties == null) { 50 | return null; 51 | } else { 52 | TreeMap ans = new TreeMap(); 53 | 54 | for (String key : properties.getPropertyKeys()) { 55 | ans.put(key, properties.getProperty(key)); 56 | } 57 | 58 | return ans; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/tx/BitsyTransactionContext.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.tx; 2 | 3 | import com.lambdazen.bitsy.BitsyEdge; 4 | import com.lambdazen.bitsy.BitsyException; 5 | import com.lambdazen.bitsy.BitsyVertex; 6 | import com.lambdazen.bitsy.IEdge; 7 | import com.lambdazen.bitsy.IGraphStore; 8 | import com.lambdazen.bitsy.UUID; 9 | import com.lambdazen.bitsy.store.AdjacencyMap; 10 | import com.lambdazen.bitsy.store.IEdgeRemover; 11 | import java.util.ArrayList; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Optional; 16 | import java.util.function.Consumer; 17 | import org.apache.tinkerpop.gremlin.structure.Transaction; 18 | import org.apache.tinkerpop.gremlin.structure.Transaction.CLOSE_BEHAVIOR; 19 | import org.apache.tinkerpop.gremlin.structure.Transaction.READ_WRITE_BEHAVIOR; 20 | import org.apache.tinkerpop.gremlin.structure.Transaction.Status; 21 | 22 | public class BitsyTransactionContext { 23 | Map unmodifiedVertices; 24 | Map unmodifiedEdges; 25 | Map changedVertices; 26 | Map changedEdges; 27 | IGraphStore store; 28 | AdjacencyMap adjMap; 29 | List> transactionListeners; 30 | 31 | Consumer readWriteConsumer = READ_WRITE_BEHAVIOR.AUTO; 32 | 33 | // The default close behavior in TP3 changed to rollback from commit in TP2 34 | Consumer closeConsumer = CLOSE_BEHAVIOR.ROLLBACK; 35 | 36 | public BitsyTransactionContext(IGraphStore store) { 37 | this.unmodifiedVertices = new HashMap(); 38 | this.unmodifiedEdges = new HashMap(); 39 | this.changedVertices = new HashMap(); 40 | this.changedEdges = new HashMap(); 41 | this.store = store; 42 | this.transactionListeners = new ArrayList>(); 43 | 44 | this.adjMap = new AdjacencyMap(false, new IEdgeRemover() { 45 | @Override 46 | public IEdge removeEdge(UUID id) { 47 | return removeEdgeOnVertexDelete(id); 48 | } 49 | }); 50 | } 51 | 52 | // This method is called to remove an edge through the IEdgeRemover 53 | private IEdge removeEdgeOnVertexDelete(UUID edgeId) throws BitsyException { 54 | // This is called from remove on adjMap, which means that the edge was added in this Tx 55 | BitsyEdge edge = changedEdges.remove(edgeId); 56 | 57 | // Only an edge that is present in this Tx can be removed by the IEdgeRemover 58 | assert (edge != null); 59 | 60 | return edge; 61 | } 62 | 63 | public void addTransactionListener(Consumer listener) { 64 | transactionListeners.add(listener); 65 | } 66 | 67 | public void removeTransactionListener(Consumer listener) { 68 | transactionListeners.remove(listener); 69 | } 70 | 71 | public void clearTransactionListeners() { 72 | transactionListeners.clear(); 73 | } 74 | 75 | public void announceCommit(BitsyTransaction t) { 76 | this.transactionListeners.forEach(c -> c.accept(Status.COMMIT)); 77 | } 78 | 79 | public void announceRollback(BitsyTransaction t) { 80 | this.transactionListeners.forEach(c -> c.accept(Status.ROLLBACK)); 81 | } 82 | 83 | public void onReadWrite(Consumer consumer) { 84 | readWriteConsumer = 85 | Optional.ofNullable(consumer).orElseThrow(Transaction.Exceptions::onReadWriteBehaviorCannotBeNull); 86 | } 87 | 88 | public void onClose(Consumer consumer) { 89 | closeConsumer = Optional.ofNullable(consumer).orElseThrow(Transaction.Exceptions::onCloseBehaviorCannotBeNull); 90 | } 91 | 92 | public Consumer getReadWriteConsumer() { 93 | return readWriteConsumer; 94 | } 95 | 96 | public Consumer getCloseConsumer() { 97 | return closeConsumer; 98 | } 99 | 100 | public void clear() { 101 | unmodifiedVertices.clear(); 102 | unmodifiedEdges.clear(); 103 | changedVertices.clear(); 104 | changedEdges.clear(); 105 | adjMap.clear(); 106 | 107 | // Don't clear the long-lived subscriptions, viz. transactionListeners, readWriteConsumer and closeConsumer 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/util/BitsyElementIterator.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.util; 2 | 3 | import com.lambdazen.bitsy.BitsyElement; 4 | import com.lambdazen.bitsy.BitsyState; 5 | import com.lambdazen.bitsy.UUID; 6 | import java.util.Collection; 7 | import java.util.HashSet; 8 | import java.util.Iterator; 9 | import java.util.NoSuchElementException; 10 | import org.apache.tinkerpop.gremlin.structure.Element; 11 | 12 | public abstract class BitsyElementIterator implements Iterator { 13 | private Iterator changedElemIter; 14 | private Iterator elementIter; 15 | private HashSet changedIds; 16 | private ElementType readAhead; 17 | private HashSet allChangedIds; 18 | 19 | public BitsyElementIterator(Collection vertices, Iterator changedElemIter) { 20 | this.elementIter = vertices.iterator(); 21 | this.changedIds = new HashSet(); 22 | this.readAhead = null; 23 | this.changedElemIter = changedElemIter; 24 | } 25 | 26 | public BitsyElementIterator( 27 | Collection vertices, 28 | Iterator changedElemIter, 29 | Collection allChangedVertices) { 30 | this.elementIter = vertices.iterator(); 31 | this.changedIds = null; 32 | this.readAhead = null; 33 | this.changedElemIter = changedElemIter; 34 | this.allChangedIds = new HashSet(); 35 | for (Element elem : allChangedVertices) { 36 | allChangedIds.add((UUID) elem.id()); 37 | } 38 | } 39 | 40 | public abstract UUID getId(BeanType bean); 41 | 42 | public abstract ElementType getElement(BeanType bean); 43 | 44 | public boolean hasNext() { 45 | // First return the changed vertices 46 | while ((readAhead == null) && (changedElemIter.hasNext())) { 47 | ElementType elem = changedElemIter.next(); 48 | 49 | // Don't return this ID again 50 | if (changedIds != null) { 51 | changedIds.add((UUID) elem.id()); 52 | } 53 | 54 | // Skip over deleted vertices 55 | if (((BitsyElement) elem).getState() != BitsyState.D) { 56 | // Found a good element 57 | readAhead = elem; 58 | break; 59 | } 60 | } 61 | 62 | // Found it? 63 | if (readAhead != null) return true; 64 | 65 | // Otherwise make sure that the next available vertex is not one of the changed IDs 66 | while ((readAhead == null) && (elementIter.hasNext())) { 67 | BeanType bean = elementIter.next(); 68 | if (changedIds != null) { 69 | if (!changedIds.contains(getId(bean))) { 70 | // A new vertex 71 | readAhead = getElement(bean); 72 | } 73 | } else { 74 | // For indexes, we can't be sure what the diffs are. So any changed ID must be ignored. 75 | if (!allChangedIds.contains(getId(bean))) { 76 | // if ((changedIds == null) || !changedIds.contains(getId(bean))) { 77 | // A new vertex 78 | readAhead = getElement(bean); 79 | } 80 | } 81 | } 82 | 83 | return (readAhead != null); 84 | } 85 | 86 | public ElementType next() { 87 | if (readAhead == null) { 88 | // Running hasNext() in case the caller did not call it 89 | hasNext(); 90 | } 91 | 92 | ElementType ans = readAhead; 93 | if (ans == null) { 94 | // Still couldn't find it 95 | throw new NoSuchElementException(); 96 | } else { 97 | // Go back and get the correct version from the transaction 98 | readAhead = null; 99 | return ans; 100 | } 101 | } 102 | 103 | public void remove() { 104 | throw new UnsupportedOperationException(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/util/BufferFlusher.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.util; 2 | 3 | import com.lambdazen.bitsy.BitsyException; 4 | import com.lambdazen.bitsy.util.DoubleBuffer.BufferName; 5 | import java.util.List; 6 | 7 | /** This interface represents a flush worker that takes can empty a buffer (A/B) */ 8 | public interface BufferFlusher { 9 | // Any exception thrown by this method will stop further enqueues. 10 | // InterruptedExceptions must be rethrown to kill the flush thread 11 | public void flushBuffer(BufferName bufName, List workList) throws BitsyException, InterruptedException; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/util/BufferPotential.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.util; 2 | 3 | /** 4 | * This interface represents a buffer potential that will be updated on each 5 | * enqueue. The implementing class need not be thread-safe, but the DoubleBuffer 6 | * will explicitly synchronize on this object before calling addWork or reset(). 7 | * The implementing class can have other synchronized methods to reconfigure 8 | * itself 9 | */ 10 | public interface BufferPotential { 11 | /** 12 | * This method is invoked on each enqueue with the additional work 13 | * potential. If the total work reaches a threshold, it will return true. 14 | * Otherwise, it can return false. 15 | */ 16 | public boolean addWork(T newWork); 17 | 18 | /** 19 | * This method is called to reset the potential, when the double buffer 20 | * flips the enqueue buffer 21 | */ 22 | public void reset(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/util/BufferQueuer.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.util; 2 | 3 | import com.lambdazen.bitsy.BitsyException; 4 | import com.lambdazen.bitsy.util.DoubleBuffer.BufferName; 5 | 6 | /** This interface represents a flush worker that takes can empty a buffer (A/B) */ 7 | public interface BufferQueuer { 8 | // Any exception thrown by this method will stop further enqueues. 9 | // InterruptedExceptions must be rethrown to kill the flush thread 10 | public void onQueue(BufferName bufName, T work) throws BitsyException; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/util/DefaultCommitChanges.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.util; 2 | 3 | import com.lambdazen.bitsy.BitsyEdge; 4 | import com.lambdazen.bitsy.BitsyException; 5 | import com.lambdazen.bitsy.BitsyVertex; 6 | import com.lambdazen.bitsy.ICommitChanges; 7 | import java.util.ArrayList; 8 | import java.util.Collection; 9 | import java.util.List; 10 | 11 | public class DefaultCommitChanges implements ICommitChanges { 12 | private List changedVertices; 13 | private List changedEdges; 14 | 15 | public DefaultCommitChanges() { 16 | this.changedVertices = new ArrayList(); 17 | this.changedEdges = new ArrayList(); 18 | } 19 | 20 | @Override 21 | public Collection getVertexChanges() { 22 | return changedVertices; 23 | } 24 | 25 | @Override 26 | public Collection getEdgeChanges() { 27 | return changedEdges; 28 | } 29 | 30 | public void reset() { 31 | // Clear retains the size from previous round, which is useful to load V/E logs 32 | changedVertices.clear(); 33 | changedEdges.clear(); 34 | } 35 | 36 | public void changeVertex(BitsyVertex vertex) throws BitsyException { 37 | changedVertices.add(vertex); 38 | } 39 | 40 | public void changeEdge(BitsyEdge edge) throws BitsyException { 41 | changedEdges.add(edge); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/util/DoubleBuffer.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.util; 2 | 3 | import com.lambdazen.bitsy.BitsyException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | * This class implements a double buffer that lets one set of Threads enqueue 11 | * work on one queue while a "flush thread" performs the work queued up on the 12 | * other queue 13 | */ 14 | public class DoubleBuffer { 15 | private static final Logger log = LoggerFactory.getLogger(DoubleBuffer.class); 16 | 17 | public enum BufferName { 18 | A, 19 | B 20 | }; 21 | 22 | // State of the double buffer 23 | int enqueueIdx; 24 | boolean[] needFlush; 25 | List workListA; 26 | List workListB; 27 | 28 | BitsyException toThrow; 29 | boolean trackWork; 30 | 31 | // Helper objects 32 | BufferPotential pot; 33 | DoubleBufferThread flushThread; 34 | 35 | /** 36 | * This constructor takes an executor service on which the FlushWorker will 37 | * be called when it is time to flush the buffer. 38 | */ 39 | public DoubleBuffer(BufferPotential initPot, BufferFlusher flusher, String flushThreadName) { 40 | this(initPot, flusher, flushThreadName, true, false); 41 | } 42 | 43 | public DoubleBuffer( 44 | BufferPotential initPot, 45 | BufferFlusher flusher, 46 | String flushThreadName, 47 | boolean trackWork, 48 | boolean syncMode) { 49 | this.enqueueIdx = 0; 50 | 51 | this.pot = initPot; 52 | this.needFlush = new boolean[] {false, false}; 53 | this.flushThread = new DoubleBufferThread(flushThreadName, this, flusher, syncMode); 54 | this.trackWork = trackWork; 55 | 56 | if (trackWork) { 57 | this.workListA = new ArrayList(); 58 | this.workListB = new ArrayList(); 59 | } 60 | 61 | flushThread.start(); 62 | } 63 | 64 | public BufferPotential getPot() { 65 | return pot; 66 | } 67 | 68 | public void stop(int joinTimeout) { 69 | if (flushThread != null) { 70 | synchronized (pot) { 71 | needFlush[0] = true; 72 | needFlush[1] = true; 73 | pot.notifyAll(); 74 | flushThread.safeStop(); 75 | } 76 | 77 | try { 78 | flushThread.join(joinTimeout); 79 | 80 | flushThread.interrupt(); 81 | flushThread.join(joinTimeout); 82 | } catch (InterruptedException e) { 83 | // Some other thread interrupted this one 84 | log.error(Thread.currentThread().getName() + " was interrupted during stop() by a different thread", e); 85 | } 86 | 87 | flushThread = null; 88 | } 89 | } 90 | 91 | public void addWork(final T work) throws BitsyException { 92 | synchronized (pot) { 93 | // There is actual work being enqueued by a thread 94 | if (toThrow != null) { 95 | throw toThrow; 96 | } 97 | 98 | if (trackWork) { 99 | (enqueueIdx == 0 ? workListA : workListB).add(work); 100 | } 101 | 102 | boolean needFlushThisTime = pot.addWork(work); 103 | this.needFlush[enqueueIdx] = needFlush[enqueueIdx] || needFlushThisTime; 104 | 105 | if (needFlushThisTime) { 106 | pot.notifyAll(); 107 | } 108 | } 109 | } 110 | 111 | public BufferName getBufferToFlush() throws InterruptedException { 112 | synchronized (pot) { 113 | // Flush if need to flush is true on the enqueue index 114 | while (!needFlush[enqueueIdx]) { 115 | pot.wait(); 116 | } 117 | 118 | // Flush buffer is now the enqueue index 119 | BufferName flushBuf = getEnqueueBuffer(); 120 | 121 | // The enqueue buffer moves to the other buffer 122 | enqueueIdx = 1 - enqueueIdx; 123 | 124 | // Now that we have moved to the other queue, the potential function can be reset 125 | pot.reset(); 126 | 127 | return flushBuf; 128 | } 129 | } 130 | 131 | public List getWorkList(BufferName bufName) { 132 | // This method will return null if the object was initialized with trackWork = false 133 | return (bufName == BufferName.A) ? workListA : workListB; 134 | } 135 | 136 | public BufferName getEnqueueBuffer() { 137 | return (enqueueIdx == 0) ? BufferName.A : BufferName.B; 138 | } 139 | 140 | public void completedFlush() { 141 | synchronized (pot) { 142 | // Done with the flush on the 'other' queue 143 | needFlush[1 - enqueueIdx] = false; 144 | 145 | // Clear the work list 146 | if (trackWork) { 147 | ((enqueueIdx == 0) ? workListB : workListA).clear(); 148 | } 149 | } 150 | } 151 | 152 | public void setException(BitsyException bitsyException) { 153 | synchronized (pot) { 154 | toThrow = bitsyException; 155 | } 156 | } 157 | 158 | public BufferPotential getPotential() { 159 | return pot; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/util/DoubleBufferThread.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.util; 2 | 3 | import com.lambdazen.bitsy.BitsyErrorCodes; 4 | import com.lambdazen.bitsy.BitsyException; 5 | import com.lambdazen.bitsy.util.DoubleBuffer.BufferName; 6 | import java.util.List; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** This class flushes the double buffer based on the potential function provided to it */ 11 | public class DoubleBufferThread extends Thread { 12 | private static final Logger log = LoggerFactory.getLogger(DoubleBuffer.class); 13 | 14 | DoubleBuffer buf; 15 | BufferFlusher flush; 16 | boolean syncMode; 17 | boolean stopped; 18 | 19 | public DoubleBufferThread(String threadName, DoubleBuffer buf, BufferFlusher flush, boolean syncMode) { 20 | super(threadName); 21 | setDaemon(true); 22 | 23 | this.buf = buf; 24 | this.flush = flush; 25 | this.syncMode = syncMode; 26 | this.stopped = false; 27 | } 28 | 29 | public void safeStop() { 30 | this.stopped = true; 31 | } 32 | 33 | public void run() { 34 | try { 35 | while (!stopped) { 36 | if (syncMode) { 37 | synchronized (buf.getPotential()) { 38 | doFlush(); 39 | } 40 | } else { 41 | doFlush(); 42 | } 43 | } 44 | } catch (InterruptedException e) { 45 | // Exiting thread 46 | log.error( 47 | Thread.currentThread().getName() 48 | + " was interrupted, most likely because a safe stop was not possible. This may result in recovery-related warnings during the next startup", 49 | e); 50 | } 51 | } 52 | 53 | public void doFlush() throws InterruptedException { 54 | BufferName bufToFlush = buf.getBufferToFlush(); 55 | 56 | if (stopped) { 57 | // Exit 58 | return; 59 | } 60 | 61 | List workList = buf.getWorkList(bufToFlush); 62 | 63 | // Invoke the new flusher on the dequeue buffer 64 | try { 65 | flush.flushBuffer(bufToFlush, workList); 66 | } catch (BitsyException e) { 67 | BitsyException bitsyException = new BitsyException( 68 | BitsyErrorCodes.EXCEPTION_IN_FLUSH, "Encountered exception in thread " + getName(), e); 69 | buf.setException(bitsyException); 70 | log.error(getName() + " encountered an unrecoverable exception", bitsyException); 71 | 72 | // Exit to avoid completing the flush. The next time may have a chance. 73 | return; 74 | } 75 | 76 | // Don't flush the next time -- till the potential function triggers 77 | buf.completedFlush(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/util/DoubleBufferWithExecWork.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.util; 2 | 3 | import com.lambdazen.bitsy.BitsyException; 4 | 5 | public class DoubleBufferWithExecWork extends DoubleBuffer { 6 | BufferQueuer queuer; 7 | 8 | public DoubleBufferWithExecWork( 9 | BufferPotential initPot, 10 | BufferQueuer queuer, 11 | BufferFlusher flusher, 12 | String flushThreadName, 13 | boolean trackWork, 14 | boolean syncMode, 15 | BufferName initBuffer) { 16 | super(initPot, flusher, flushThreadName, trackWork, syncMode); 17 | this.queuer = queuer; 18 | 19 | // The starting buffer is based on initBuffer 20 | this.enqueueIdx = (initBuffer == BufferName.A) ? 0 : 1; 21 | } 22 | 23 | public void addAndExecuteWork(final T work) throws BitsyException { 24 | synchronized (pot) { 25 | // Do the work inside the synchronized block so that a buffer 26 | // doesn't get swapped out in the middle 27 | queuer.onQueue(getEnqueueBuffer(), work); 28 | 29 | // The work object may be modified in the previous step 30 | addWork(work); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/util/EdgeIterator.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.util; 2 | 3 | import com.lambdazen.bitsy.BitsyEdge; 4 | import com.lambdazen.bitsy.UUID; 5 | import com.lambdazen.bitsy.store.EdgeBean; 6 | import com.lambdazen.bitsy.tx.BitsyTransaction; 7 | import java.util.Collection; 8 | import java.util.Iterator; 9 | 10 | public class EdgeIterator extends BitsyElementIterator implements Iterator { 11 | private BitsyTransaction tx; 12 | 13 | public EdgeIterator(BitsyTransaction tx, Collection txEdges, Collection vertices) { 14 | super(vertices, txEdges.iterator()); 15 | 16 | this.tx = tx; 17 | } 18 | 19 | public EdgeIterator( 20 | BitsyTransaction tx, 21 | Collection txEdges, 22 | Collection vertices, 23 | Collection allTxEdges) { 24 | super(vertices, txEdges.iterator(), allTxEdges); 25 | 26 | this.tx = tx; 27 | } 28 | 29 | @Override 30 | public UUID getId(EdgeBean bean) { 31 | return bean.getId(); 32 | } 33 | 34 | @Override 35 | public BitsyEdge getElement(EdgeBean bean) { 36 | return (BitsyEdge) tx.getEdge(bean.getId()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/util/VertexIterator.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.util; 2 | 3 | import com.lambdazen.bitsy.BitsyVertex; 4 | import com.lambdazen.bitsy.UUID; 5 | import com.lambdazen.bitsy.store.VertexBean; 6 | import com.lambdazen.bitsy.tx.BitsyTransaction; 7 | import java.util.Collection; 8 | import java.util.Iterator; 9 | 10 | public class VertexIterator extends BitsyElementIterator implements Iterator { 11 | private BitsyTransaction tx; 12 | 13 | public VertexIterator(BitsyTransaction tx, Collection txVertices, Collection vertices) { 14 | super(vertices, txVertices.iterator()); 15 | 16 | this.tx = tx; 17 | } 18 | 19 | public VertexIterator( 20 | BitsyTransaction tx, 21 | Collection txVertices, 22 | Collection vertices, 23 | Collection allChangedVertices) { 24 | super(vertices, txVertices.iterator(), allChangedVertices); 25 | 26 | this.tx = tx; 27 | } 28 | 29 | @Override 30 | public UUID getId(VertexBean bean) { 31 | return bean.getId(); 32 | } 33 | 34 | @Override 35 | public BitsyVertex getElement(VertexBean bean) { 36 | return (BitsyVertex) tx.getVertex(bean.getId()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/wrapper/BitsyAutoReloadingEdge.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.wrapper; 2 | 3 | import com.lambdazen.bitsy.BitsyEdge; 4 | import com.lambdazen.bitsy.BitsyGraph; 5 | import com.lambdazen.bitsy.tx.BitsyTransaction; 6 | import java.util.Iterator; 7 | import org.apache.tinkerpop.gremlin.structure.Direction; 8 | import org.apache.tinkerpop.gremlin.structure.Edge; 9 | import org.apache.tinkerpop.gremlin.structure.Graph; 10 | import org.apache.tinkerpop.gremlin.structure.Property; 11 | import org.apache.tinkerpop.gremlin.structure.Vertex; 12 | import org.apache.tinkerpop.gremlin.structure.util.StringFactory; 13 | 14 | public class BitsyAutoReloadingEdge implements Edge { 15 | BitsyEdge edge; 16 | BitsyGraph graph; 17 | 18 | public BitsyAutoReloadingEdge(BitsyGraph g, BitsyEdge e) { 19 | this.edge = e; 20 | this.graph = g; 21 | } 22 | 23 | public Edge getBaseEdge() { 24 | if (((BitsyTransaction) edge.getTransaction()).isStopped()) { 25 | edge = (BitsyEdge) graph.edges(edge.id()).next(); 26 | } 27 | 28 | return edge; 29 | } 30 | 31 | @Override 32 | public void remove() { 33 | getBaseEdge().remove(); 34 | } 35 | 36 | @Override 37 | public Object id() { 38 | // Don't reload just for the ID 39 | return edge.id(); 40 | } 41 | 42 | @Override 43 | public String label() { 44 | return getBaseEdge().label(); 45 | } 46 | 47 | public int hashCode() { 48 | return getBaseEdge().hashCode(); 49 | } 50 | 51 | public boolean equals(Object o) { 52 | return getBaseEdge().equals(o); 53 | } 54 | 55 | public String toString() { 56 | return StringFactory.edgeString(this); 57 | } 58 | 59 | @Override 60 | public Graph graph() { 61 | return getBaseEdge().graph(); 62 | } 63 | 64 | @Override 65 | public Property property(String key, V value) { 66 | return getBaseEdge().property(key, value); 67 | } 68 | 69 | @Override 70 | public Iterator vertices(Direction direction) { 71 | return new BitsyAutoReloadingGraph.VertexIterator(graph, getBaseEdge().vertices(direction)); 72 | } 73 | 74 | @Override 75 | public Iterator> properties(String... propertyKeys) { 76 | return getBaseEdge().properties(propertyKeys); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/wrapper/BitsyAutoReloadingGraph.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.wrapper; 2 | 3 | import com.lambdazen.bitsy.BitsyEdge; 4 | import com.lambdazen.bitsy.BitsyGraph; 5 | import com.lambdazen.bitsy.BitsyVertex; 6 | import java.util.Iterator; 7 | import org.apache.commons.configuration2.Configuration; 8 | import org.apache.tinkerpop.gremlin.process.computer.GraphComputer; 9 | import org.apache.tinkerpop.gremlin.structure.Edge; 10 | import org.apache.tinkerpop.gremlin.structure.Graph; 11 | import org.apache.tinkerpop.gremlin.structure.Transaction; 12 | import org.apache.tinkerpop.gremlin.structure.Vertex; 13 | 14 | public class BitsyAutoReloadingGraph implements Graph { 15 | private BitsyGraph graph; 16 | 17 | public BitsyAutoReloadingGraph(BitsyGraph g) { 18 | this.graph = g; 19 | } 20 | 21 | public BitsyGraph getBaseGraph() { 22 | return graph; 23 | } 24 | 25 | public String toString() { 26 | return "bitsyautoreloadinggraph[" + getBaseGraph().toString() + "]"; 27 | } 28 | 29 | public static final BitsyAutoReloadingGraph open(Configuration configuration) { 30 | return new BitsyAutoReloadingGraph(BitsyGraph.open(configuration)); 31 | } 32 | 33 | @Override 34 | public Vertex addVertex(Object... keyValues) { 35 | BitsyVertex base = (BitsyVertex) (graph.addVertex(keyValues)); 36 | 37 | return new BitsyAutoReloadingVertex(graph, base); 38 | } 39 | 40 | @Override 41 | public C compute(Class graphComputerClass) throws IllegalArgumentException { 42 | return (C) graph.compute(graphComputerClass); 43 | } 44 | 45 | @Override 46 | public GraphComputer compute() throws IllegalArgumentException { 47 | return graph.compute(); 48 | } 49 | 50 | @Override 51 | public Iterator vertices(Object... vertexIds) { 52 | Iterator result = graph.vertices(vertexIds); 53 | return new VertexIterator(graph, result); 54 | } 55 | 56 | @Override 57 | public Iterator edges(Object... edgeIds) { 58 | Iterator result = graph.edges(edgeIds); 59 | 60 | return new EdgeIterator(graph, result); 61 | } 62 | 63 | @Override 64 | public Transaction tx() { 65 | return graph.tx(); 66 | } 67 | 68 | @Override 69 | public void close() throws Exception { 70 | graph.close(); 71 | } 72 | 73 | @Override 74 | public Variables variables() { 75 | return graph.variables(); 76 | } 77 | 78 | @Override 79 | public Configuration configuration() { 80 | return graph.configuration(); 81 | } 82 | 83 | @Override 84 | public Graph.Features features() { 85 | return graph.features(); 86 | } 87 | 88 | public static class VertexIterator implements Iterator { 89 | BitsyGraph graph; 90 | Iterator iter; 91 | 92 | public VertexIterator(BitsyGraph g, Iterator iter) { 93 | this.graph = g; 94 | this.iter = iter; 95 | } 96 | 97 | @Override 98 | public boolean hasNext() { 99 | return iter.hasNext(); 100 | } 101 | 102 | @Override 103 | public Vertex next() { 104 | return new BitsyAutoReloadingVertex(graph, (BitsyVertex) (iter.next())); 105 | } 106 | 107 | @Override 108 | public void remove() { 109 | iter.remove(); 110 | } 111 | } 112 | 113 | public static class EdgeIterator implements Iterator { 114 | BitsyGraph graph; 115 | Iterator iter; 116 | 117 | public EdgeIterator(BitsyGraph g, Iterator iter) { 118 | this.graph = g; 119 | this.iter = iter; 120 | } 121 | 122 | @Override 123 | public boolean hasNext() { 124 | return iter.hasNext(); 125 | } 126 | 127 | @Override 128 | public Edge next() { 129 | return new BitsyAutoReloadingEdge(graph, (BitsyEdge) (iter.next())); 130 | } 131 | 132 | @Override 133 | public void remove() { 134 | iter.remove(); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/com/lambdazen/bitsy/wrapper/BitsyAutoReloadingVertex.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.wrapper; 2 | 3 | import com.lambdazen.bitsy.BitsyEdge; 4 | import com.lambdazen.bitsy.BitsyGraph; 5 | import com.lambdazen.bitsy.BitsyVertex; 6 | import com.lambdazen.bitsy.tx.BitsyTransaction; 7 | import java.util.Iterator; 8 | import org.apache.tinkerpop.gremlin.structure.Direction; 9 | import org.apache.tinkerpop.gremlin.structure.Edge; 10 | import org.apache.tinkerpop.gremlin.structure.Graph; 11 | import org.apache.tinkerpop.gremlin.structure.Vertex; 12 | import org.apache.tinkerpop.gremlin.structure.VertexProperty; 13 | import org.apache.tinkerpop.gremlin.structure.VertexProperty.Cardinality; 14 | import org.apache.tinkerpop.gremlin.structure.util.StringFactory; 15 | 16 | public class BitsyAutoReloadingVertex implements Vertex { 17 | BitsyVertex vertex; 18 | BitsyGraph graph; 19 | 20 | public BitsyAutoReloadingVertex(BitsyGraph g, BitsyVertex v) { 21 | this.vertex = v; 22 | this.graph = g; 23 | } 24 | 25 | public Vertex getBaseVertex() { 26 | if (((BitsyTransaction) vertex.getTransaction()).isStopped()) { 27 | vertex = (BitsyVertex) graph.vertices(vertex.id()).next(); 28 | } 29 | 30 | return vertex; 31 | } 32 | 33 | @Override 34 | public Object id() { 35 | // Don't reload just for the ID 36 | return vertex.id(); 37 | } 38 | 39 | @Override 40 | public String label() { 41 | return getBaseVertex().label(); 42 | } 43 | 44 | public int hashCode() { 45 | return getBaseVertex().hashCode(); 46 | } 47 | 48 | public boolean equals(Object o) { 49 | return getBaseVertex().equals(o); 50 | } 51 | 52 | public String toString() { 53 | return StringFactory.vertexString(this); 54 | } 55 | 56 | @Override 57 | public Graph graph() { 58 | return graph; 59 | } 60 | 61 | @Override 62 | public void remove() { 63 | getBaseVertex().remove(); 64 | } 65 | 66 | @Override 67 | public Edge addEdge(String label, Vertex inVertex, Object... keyValues) { 68 | return new BitsyAutoReloadingEdge(graph, (BitsyEdge) (getBaseVertex().addEdge(label, inVertex, keyValues))); 69 | } 70 | 71 | @Override 72 | public Iterator edges(Direction direction, String... labels) { 73 | return new BitsyAutoReloadingGraph.EdgeIterator(graph, getBaseVertex().edges(direction, labels)); 74 | } 75 | 76 | @Override 77 | public Iterator vertices(Direction direction, String... labels) { 78 | return new BitsyAutoReloadingGraph.VertexIterator(graph, getBaseVertex().vertices(direction, labels)); 79 | } 80 | 81 | @Override 82 | public VertexProperty property(String key) { 83 | return getBaseVertex().property(key); 84 | } 85 | 86 | @Override 87 | public VertexProperty property(String key, V value) { 88 | return getBaseVertex().property(key, value); 89 | } 90 | 91 | @Override 92 | public VertexProperty property(Cardinality cardinality, String key, V value, Object... keyValues) { 93 | return getBaseVertex().property(cardinality, key, value, keyValues); 94 | } 95 | 96 | @Override 97 | public Iterator> properties(String... propertyKeys) { 98 | return getBaseVertex().properties(propertyKeys); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/org.apache.tinkerpop.gremlin.jsr223.GremlinPlugin: -------------------------------------------------------------------------------- 1 | com.lambdazen.bitsy.jsr223.BitsyGremlinPlugin 2 | -------------------------------------------------------------------------------- /src/test/java/com/lambdazen/bitsy/BitsyMemGraphIT.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import com.lambdazen.bitsy.store.Record; 4 | import com.lambdazen.bitsy.store.Record.RecordType; 5 | import org.apache.tinkerpop.gremlin.structure.Direction; 6 | import org.apache.tinkerpop.gremlin.structure.Edge; 7 | import org.apache.tinkerpop.gremlin.structure.Vertex; 8 | 9 | public class BitsyMemGraphIT extends BitsyGraphIT { 10 | public boolean isPersistent() { 11 | return false; 12 | } 13 | 14 | public void setUp() { 15 | System.out.println("Setting up memory-only graph"); 16 | graph = new BitsyGraph(false); 17 | } 18 | 19 | public void tearDown() { 20 | System.out.println("Tearing down graph"); 21 | try { 22 | graph.close(); 23 | } catch (Exception e) { 24 | throw new RuntimeException("Couldn't close", e); 25 | } 26 | } 27 | 28 | public void testPersistence() { 29 | // Disable 30 | } 31 | 32 | public void testObsolescence() { 33 | IGraphStore store = ((BitsyGraph) graph).getStore(); 34 | 35 | // Create a vertex 36 | Vertex v = graph.addVertex(); 37 | Object vid = v.id(); 38 | v.property("foo", "bar"); 39 | 40 | // Self edge 41 | Edge e = v.addEdge("self", v); 42 | Object eid = e.id(); 43 | 44 | graph.tx().commit(); 45 | 46 | Record v1MRec = new Record(RecordType.V, "{\"id\":\"" + vid + "\",\"v\":1,\"s\":\"M\"}"); 47 | assertFalse(v1MRec.checkObsolete(store, false, 1, null)); 48 | assertFalse(v1MRec.checkObsolete(store, true, 1, null)); 49 | 50 | Record e1MRec = new Record( 51 | RecordType.E, 52 | "{\"id\":\"" + eid + "\",\"v\":1,\"s\":\"M\",\"o\":\"" + vid + "\",\"l\":\"" + vid + "\",\"i\":\"" + vid 53 | + "\"}"); 54 | assertFalse(e1MRec.checkObsolete(store, false, 1, null)); 55 | assertFalse(e1MRec.checkObsolete(store, true, 1, null)); 56 | 57 | // Create a vertex 58 | v = graph.vertices(vid).next(); 59 | v.property("foo", "baz"); 60 | 61 | e = v.edges(Direction.IN, "self").next(); 62 | e.property("foo", "baz"); 63 | 64 | graph.tx().commit(); 65 | 66 | Record v2MRec = new Record(RecordType.V, "{\"id\":\"" + vid + "\",\"v\":2,\"s\":\"M\"}"); 67 | Record v1DRec = new Record(RecordType.V, "{\"id\":\"" + vid + "\",\"v\":1,\"s\":\"D\"}"); 68 | 69 | assertTrue(v1MRec.checkObsolete(store, false, 1, null)); 70 | assertTrue(v1MRec.checkObsolete(store, true, 1, null)); 71 | 72 | assertFalse(v1DRec.checkObsolete(store, false, 1, null)); 73 | assertTrue(v1DRec.checkObsolete(store, true, 1, null)); 74 | 75 | assertFalse(v2MRec.checkObsolete(store, false, 1, null)); 76 | assertFalse(v2MRec.checkObsolete(store, true, 1, null)); 77 | 78 | Record e2MRec = new Record( 79 | RecordType.E, 80 | "{\"id\":\"" + eid + "\",\"v\":2,\"s\":\"M\",\"o\":\"" + vid + "\",\"l\":\"" + vid + "\",\"i\":\"" + vid 81 | + "\"}"); 82 | Record e1DRec = new Record( 83 | RecordType.E, 84 | "{\"id\":\"" + eid + "\",\"v\":1,\"s\":\"D\",\"o\":\"" + vid + "\",\"l\":\"" + vid + "\",\"i\":\"" + vid 85 | + "\"}"); 86 | 87 | assertTrue(e1MRec.checkObsolete(store, false, 1, null)); 88 | assertTrue(e1MRec.checkObsolete(store, true, 1, null)); 89 | 90 | assertFalse(e1DRec.checkObsolete(store, false, 1, null)); 91 | assertTrue(e1DRec.checkObsolete(store, true, 1, null)); 92 | 93 | assertFalse(e2MRec.checkObsolete(store, false, 1, null)); 94 | assertFalse(e2MRec.checkObsolete(store, true, 1, null)); 95 | 96 | // Delete vertex 97 | v = graph.vertices(vid).next(); 98 | v.remove(); 99 | 100 | // Edge will get deleted automatically! 101 | 102 | graph.tx().commit(); 103 | 104 | Record v2DRec = new Record(RecordType.V, "{\"id\":\"" + vid + "\",\"v\":1,\"s\":\"D\"}"); 105 | assertFalse(v2DRec.checkObsolete(store, false, 1, null)); 106 | assertTrue(v2DRec.checkObsolete(store, true, 1, null)); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/test/java/com/lambdazen/bitsy/FileBasedTestCase.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.net.URL; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import junit.framework.TestCase; 10 | 11 | public class FileBasedTestCase extends TestCase { 12 | public Path tempDir(String dirName) throws IOException { 13 | return tempDir(dirName, true); 14 | } 15 | 16 | public Path tempDir(String dirName, boolean delete) throws IOException { 17 | String clsUri = this.getClass().getName().replace('.', '/') + ".class"; 18 | URL url = this.getClass().getClassLoader().getResource(clsUri); 19 | String clsPath = url.getPath(); 20 | System.out.println(clsPath); 21 | String dir = clsPath.substring(clsPath.indexOf(":") + 1, clsPath.length() - clsUri.length()); 22 | System.out.println(dir); 23 | Path root = Paths.get(dir).resolve("../" + dirName).normalize(); 24 | System.out.println(root); 25 | 26 | if (!Files.exists(root)) { 27 | Files.createDirectory(root); 28 | } 29 | 30 | if (delete) { 31 | deleteDirectory(root.toFile(), false); 32 | } 33 | 34 | return root; 35 | } 36 | 37 | protected static void deleteDirectory(final File directory, boolean deleteDir) { 38 | if (directory.exists()) { 39 | for (File file : directory.listFiles()) { 40 | if (file.isDirectory()) { 41 | deleteDirectory(file, true); 42 | } else { 43 | file.delete(); 44 | } 45 | } 46 | 47 | if (deleteDir) { 48 | directory.delete(); 49 | } 50 | } 51 | } 52 | 53 | public void testDummy() throws IOException { 54 | tempDir("temp-dir"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/com/lambdazen/bitsy/RecoveryTest.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy; 2 | 3 | import com.lambdazen.bitsy.store.LoadTask; 4 | import java.net.URL; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | import java.nio.file.StandardCopyOption; 9 | import java.util.Iterator; 10 | 11 | public class RecoveryTest extends FileBasedTestCase { 12 | public RecoveryTest() {} 13 | 14 | public void testPartialTx() throws Exception { 15 | for (long minSizeForParallelLoader : new long[] {10, 1024 * 1024}) { 16 | // Try with both the parallel and serial loaders 17 | LoadTask.MIN_SIZE_FOR_PARALLEL_LOADER = minSizeForParallelLoader; 18 | 19 | URL url = this.getClass().getClassLoader().getResource("recovery"); 20 | Path rootPath = Paths.get(url.toURI()); 21 | System.out.println("Path = " + rootPath); 22 | 23 | String[] fileNames = 24 | new String[] {"txA.txt", "txB.txt", "vA.txt", "vB.txt", "eA.txt", "eB.txt", "metaA.txt", "metaB.txt" 25 | }; 26 | 27 | Path targetDir = tempDir("recoverytest"); 28 | String[] paths = {"stage1", "stage2", "stage3", "stage4", "stage5", "stage6", "stage7"}; 29 | 30 | int[] vCounts = new int[] {50, 50, 100, 75, 75, 101, 101}; 31 | int[] eCounts = new int[] {0, 0, 0, 0, 0, 99, 99}; 32 | 33 | for (int i = 0; i < paths.length; i++) { 34 | System.out.println("Deleting " + targetDir); 35 | deleteDirectory(targetDir.toFile(), false); 36 | 37 | Path sourceDir = rootPath.resolve("./" + paths[i]).normalize(); 38 | System.out.println("Copying from " + sourceDir); 39 | for (String fileName : fileNames) { 40 | Files.copy( 41 | sourceDir.resolve(fileName), 42 | targetDir.resolve(fileName), 43 | StandardCopyOption.REPLACE_EXISTING); 44 | } 45 | 46 | BitsyGraph graph = new BitsyGraph(targetDir); 47 | 48 | checkIterCount(graph.vertices(), vCounts[i]); 49 | checkIterCount(graph.edges(), eCounts[i]); 50 | graph.shutdown(); 51 | } 52 | } 53 | } 54 | 55 | private void checkIterCount(Iterator iter, int expectedCount) { 56 | int count = 0; 57 | while (iter.hasNext()) { 58 | iter.next(); 59 | count++; 60 | } 61 | 62 | assertEquals(expectedCount, count); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/com/lambdazen/bitsy/ads/set/CompactMultiSetMaxTest.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.ads.set; 2 | 3 | import java.util.Arrays; 4 | import junit.framework.TestCase; 5 | 6 | public class CompactMultiSetMaxTest extends TestCase { 7 | public CompactMultiSetMaxTest() {} 8 | 9 | public void testSimpleUse() { 10 | CompactMultiSetMax test = new CompactMultiSetMax(4, false); 11 | 12 | ClassifierGetter c = new ClassifierGetter() { 13 | @Override 14 | public String getClassifier(String obj) { 15 | return obj.substring(0, 3); 16 | } 17 | }; 18 | 19 | test = test.add("fooBar", c); // classifier is foo 20 | test = test.add("fooBaz", c); // classifier is foo 21 | 22 | test = test.add("barBar", c); // classifier is bar 23 | test = test.add("barBaz", c); // classifier is bar 24 | 25 | Object[] fooStuff = test.getSuperSetWithClassifier("foo"); 26 | Arrays.sort(fooStuff); 27 | assertEquals(2, fooStuff.length); 28 | assertEquals("fooBar", fooStuff[0]); 29 | assertEquals("fooBaz", fooStuff[1]); 30 | 31 | Object[] barStuff = test.getSuperSetWithClassifier("bar"); 32 | Arrays.sort(barStuff); 33 | assertEquals(2, barStuff.length); 34 | assertEquals("barBar", barStuff[0]); 35 | assertEquals("barBaz", barStuff[1]); 36 | 37 | test = test.remove("fooBar", c); 38 | test = test.remove("fooBaz", c); 39 | test = test.remove("barBaz", c); 40 | 41 | fooStuff = test.getSuperSetWithClassifier("foo"); 42 | assertEquals(0, fooStuff.length); 43 | 44 | barStuff = test.getSuperSetWithClassifier("bar"); 45 | assertEquals(1, barStuff.length); 46 | assertEquals("barBar", barStuff[0]); 47 | 48 | test = test.remove("barBar", c); 49 | 50 | for (Object elem : test.elements) { 51 | assertNull(elem); 52 | } 53 | 54 | assertEquals(0, test.getOccupiedCells()); 55 | } 56 | 57 | public void testResize() { 58 | CompactMultiSetMax test; 59 | 60 | // Repeat the test 100 times 61 | final int numEntries = 10000; 62 | for (int numRun = 0; numRun < 10; numRun++) { 63 | for (int factor : new int[] {10, 100, 1000}) { 64 | final int factorFinal = factor; 65 | ClassifierGetter c = new ClassifierGetter() { 66 | @Override 67 | public Integer getClassifier(Integer obj) { 68 | return obj % (numEntries / factorFinal); 69 | } 70 | }; 71 | 72 | test = new CompactMultiSetMax(4, false); 73 | 74 | for (int i = 0; i < numEntries; i++) { 75 | test = test.add(i, c); 76 | 77 | assertTrue(test.getOccupiedCells() > 0); 78 | } 79 | 80 | for (int key = 0; key < numEntries / factor; key++) { 81 | Object[] matches = test.getSuperSetWithClassifier(key); 82 | 83 | int count = 0; 84 | for (Object match : matches) { 85 | if (((Integer) match) % (numEntries / factor) == key) { 86 | count++; 87 | } 88 | } 89 | 90 | assertEquals(factor, count); 91 | } 92 | 93 | for (int i = 0; i < numEntries / 2; i++) { 94 | test = test.remove(i, c); 95 | 96 | // Also remove some random things that can't be there 97 | if (i % 13 == 0) { 98 | // test = test.remove(-i, c); 99 | } 100 | } 101 | 102 | for (int key = 0; key < numEntries / factor; key++) { 103 | Object[] matches = test.getSuperSetWithClassifier(key); 104 | 105 | int count = 0; 106 | for (Object match : matches) { 107 | if (((Integer) match) % (numEntries / factor) == key) { 108 | count++; 109 | } 110 | } 111 | 112 | assertEquals(factor / 2, count); 113 | } 114 | 115 | for (int i = numEntries / 2; i < numEntries; i++) { 116 | test = test.remove(i, c); 117 | } 118 | 119 | assertEquals(0, test.getOccupiedCells()); 120 | 121 | for (Object elem : test.elements) { 122 | assertNull( 123 | "For factor " + factor + " and numEntries " + numEntries + " with elements " 124 | + Arrays.asList(CompactSet.getElements(elem)) + " as " + elem, 125 | elem); 126 | } 127 | 128 | for (int key = 0; key < numEntries; key++) { 129 | Object[] matches = test.getSuperSetWithClassifier(key); 130 | 131 | assertEquals(0, matches.length); 132 | } 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/test/java/com/lambdazen/bitsy/store/EndpointTest.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import com.lambdazen.bitsy.UUID; 4 | import junit.framework.TestCase; 5 | 6 | public class EndpointTest extends TestCase { 7 | public EndpointTest() {} 8 | 9 | public void testCompare() throws Exception { 10 | UUID vid = UUID.randomUUID(); 11 | UUID eid = UUID.randomUUID(); 12 | Endpoint e1; 13 | Endpoint e2; 14 | 15 | e1 = new Endpoint(null, null); 16 | e1.setMarker(); 17 | e2 = new Endpoint(null, eid); 18 | 19 | assertTrue(e1.isMatch(e2)); 20 | assertEquals(-1, e1.compareTo(e2)); 21 | assertEquals(1, e2.compareTo(e1)); 22 | 23 | e2 = new Endpoint("something", eid); 24 | assertTrue(e1.isMatch(e2)); 25 | assertEquals(-1, e1.compareTo(e2)); 26 | assertEquals(1, e2.compareTo(e1)); 27 | assertFalse(e1.equals(e2)); 28 | 29 | // e2 = new Endpoint(eid, null, eid); 30 | // assertFalse(e1.isMatch(e2)); 31 | // assertEquals(vid.compareTo(eid), e1.compareTo(e2)); 32 | // assertEquals(-vid.compareTo(eid), e2.compareTo(e1)); 33 | // assertFalse(e1.equals(e2)); 34 | 35 | e1 = new Endpoint("foo", null); 36 | e1.setMarker(); 37 | 38 | e2 = new Endpoint("foo", eid); 39 | 40 | assertTrue(e1.isMatch(e2)); 41 | assertEquals(-1, e1.compareTo(e2)); 42 | assertEquals(1, e2.compareTo(e1)); 43 | assertFalse(e1.equals(e2)); 44 | 45 | e2 = new Endpoint("bar", eid); 46 | assertFalse(e1.isMatch(e2)); 47 | assertEquals("foo".compareTo("bar"), e1.compareTo(e2)); 48 | assertEquals("bar".compareTo("foo"), e2.compareTo(e1)); 49 | assertFalse(e1.equals(e2)); 50 | 51 | e1 = new Endpoint("foo", eid); 52 | e1.setMarker(); 53 | 54 | e2 = new Endpoint("foo", eid); 55 | assertTrue(e1.isMatch(e2)); 56 | assertEquals(-1, e1.compareTo(e2)); 57 | assertEquals(1, e2.compareTo(e1)); 58 | assertFalse(e1.equals(e2)); 59 | 60 | e2 = new Endpoint("bar", eid); 61 | assertFalse(e1.isMatch(e2)); 62 | assertEquals("foo".compareTo("bar"), e1.compareTo(e2)); 63 | assertEquals("bar".compareTo("foo"), e2.compareTo(e1)); 64 | assertFalse(e1.equals(e2)); 65 | 66 | e2 = new Endpoint("foo", vid); 67 | assertFalse(e1.isMatch(e2)); 68 | assertEquals(eid.compareTo(vid), e1.compareTo(e2)); 69 | assertEquals(-eid.compareTo(vid), e2.compareTo(e1)); 70 | assertFalse(e1.equals(e2)); 71 | 72 | e1 = new Endpoint("foo", eid); 73 | 74 | e2 = new Endpoint("foo", eid); 75 | assertEquals(0, e1.compareTo(e2)); 76 | assertEquals(e1, e2); 77 | 78 | e2 = new Endpoint("bar", eid); 79 | assertEquals("foo".compareTo("bar"), e1.compareTo(e2)); 80 | assertEquals("bar".compareTo("foo"), e2.compareTo(e1)); 81 | assertFalse(e1.equals(e2)); 82 | 83 | e2 = new Endpoint("foo", vid); 84 | assertEquals(eid.compareTo(vid), e1.compareTo(e2)); 85 | assertEquals(vid.compareTo(eid), e2.compareTo(e1)); 86 | assertFalse(e1.equals(e2)); 87 | 88 | // e2 = new Endpoint(eid, "foo", vid); 89 | // assertEquals(vid.compareTo(eid), e1.compareTo(e2)); 90 | // assertEquals(eid.compareTo(vid), e2.compareTo(e1)); 91 | // assertFalse(e1.equals(e2)); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/com/lambdazen/bitsy/store/SingleThreadedStringCanonicalizerTest.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.store; 2 | 3 | import junit.framework.TestCase; 4 | 5 | public class SingleThreadedStringCanonicalizerTest extends TestCase { 6 | public void testCanonicalize() { 7 | IStringCanonicalizer canon = new SingleThreadedStringCanonicalizer(); 8 | 9 | String abc1 = "a".concat("bc"); 10 | String abc2 = "abc"; 11 | 12 | assertEquals(abc1, abc2); 13 | assertNotSame(abc1, abc2); 14 | 15 | String c1 = canon.canonicalize(abc1); 16 | String c2 = canon.canonicalize(abc2); 17 | 18 | assertSame(c1, c2); 19 | assertSame(c1, canon.canonicalize("ab".concat("c"))); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/lambdazen/bitsy/structure/BitsyGraphStructureTestSuite.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package com.lambdazen.bitsy.structure; 20 | 21 | import com.lambdazen.bitsy.BitsyGraph; 22 | import org.apache.tinkerpop.gremlin.AbstractGremlinSuite; 23 | import org.apache.tinkerpop.gremlin.GraphProviderClass; 24 | import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine; 25 | import org.apache.tinkerpop.gremlin.structure.EdgeTest; 26 | import org.apache.tinkerpop.gremlin.structure.VertexTest; 27 | import org.junit.runner.RunWith; 28 | import org.junit.runners.model.InitializationError; 29 | import org.junit.runners.model.RunnerBuilder; 30 | 31 | /** 32 | * @author Marko A. Rodriguez (http://markorodriguez.com) 33 | */ 34 | @RunWith(BitsyGraphStructureTestSuite.class) 35 | @GraphProviderClass(provider = BitsyTestGraphProvider.class, graph = BitsyGraph.class) 36 | public class BitsyGraphStructureTestSuite extends AbstractGremlinSuite { 37 | /* 38 | * This list of tests in the suite that will be executed. Gremlin developers should add to this list 39 | * as needed to enforce tests upon implementations. 40 | */ 41 | private static final Class[] allTests = new Class[] { 42 | VertexTest.class, EdgeTest.class, 43 | // VertexPropertyTest.class, 44 | // GraphTest.class, 45 | // FeatureSupportTest.class, 46 | // PropertyTest.class, 47 | // VariablesTest.class, 48 | 49 | // DetachedGraphTest.class, 50 | // DetachedVertexPropertyTest.class, 51 | // DetachedPropertyTest.class 52 | // DetachedVertexTest.class, 53 | // DetachedEdgeTest.class, 54 | 55 | // ReferenceGraphTest.class, 56 | // ReferenceEdgeTest.class, 57 | // ReferenceVertexPropertyTest.class, 58 | // ReferenceVertexTest.class, 59 | 60 | // CommunityGeneratorTest.class, 61 | // DistributionGeneratorTest.class, 62 | // GraphConstructionTest.class, 63 | 64 | // TransactionTest.class, 65 | 66 | /* SERIALIZATION TESTS WON'T WORK -- 67 | IoPropertyTest.class, 68 | IoTest.class, 69 | IoVertexTest.class, 70 | IoCustomTest.class, 71 | IoEdgeTest.class, 72 | IoGraphTest.class, 73 | SerializationTest.class, 74 | StarGraphTest.class // has failure related to UUID and kryo 75 | */ 76 | 77 | }; 78 | 79 | public BitsyGraphStructureTestSuite(final Class klass, final RunnerBuilder builder) throws InitializationError { 80 | super(klass, builder, allTests, null, false, TraversalEngine.Type.STANDARD); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/com/lambdazen/bitsy/structure/BitsyTestGraphProvider.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.structure; 2 | 3 | import com.lambdazen.bitsy.BitsyGraph; 4 | import com.lambdazen.bitsy.BitsyIsolationLevel; 5 | import com.lambdazen.bitsy.wrapper.BitsyAutoReloadingGraph; 6 | import java.io.File; 7 | import java.nio.file.Paths; 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | import java.util.Map; 11 | import java.util.Set; 12 | import org.apache.commons.configuration2.Configuration; 13 | import org.apache.tinkerpop.gremlin.AbstractGraphProvider; 14 | import org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData; 15 | import org.apache.tinkerpop.gremlin.structure.Graph; 16 | import org.apache.tinkerpop.gremlin.structure.Transaction.READ_WRITE_BEHAVIOR; 17 | 18 | public class BitsyTestGraphProvider extends AbstractGraphProvider { 19 | private static final Set IMPLEMENTATION = new HashSet() { 20 | { 21 | add(BitsyAutoReloadingGraph.class); 22 | } 23 | }; 24 | 25 | @Override 26 | public void clear(Graph graph, Configuration configuration) throws Exception { 27 | if (graph != null) { 28 | if (graph.tx().isOpen()) graph.tx().close(); 29 | graph.tx().onReadWrite(READ_WRITE_BEHAVIOR.MANUAL); 30 | graph.tx().open(); 31 | System.out.println("Clearing graph"); 32 | graph.vertices().forEachRemaining(v -> v.remove()); 33 | graph.tx().commit(); 34 | System.out.println("Shutting down graph " + graph); 35 | graph.close(); 36 | } 37 | } 38 | 39 | @Override 40 | public Set getImplementations() { 41 | return IMPLEMENTATION; 42 | } 43 | 44 | private void wipeOut(File directory) { 45 | deleteDirectory(directory, false); 46 | } 47 | 48 | protected static void deleteDirectory(final File directory, boolean deleteDir) { 49 | if (directory.exists()) { 50 | for (File file : directory.listFiles()) { 51 | if (file.isDirectory()) { 52 | // System.out.println("Deleting dir: " + file.toString()); 53 | deleteDirectory(file, true); 54 | } else { 55 | // System.out.println("Deleting file: " + file.toString()); 56 | file.delete(); 57 | } 58 | } 59 | 60 | if (deleteDir) { 61 | // System.out.println("Deleting dir: " + directory); 62 | directory.delete(); 63 | } 64 | } 65 | // System.out.println("Exiting delete dir"); 66 | } 67 | 68 | @Override 69 | public Map getBaseConfiguration( 70 | String graphName, Class test, String testMethodName, GraphData loadGraphWith) { 71 | final String directory = makeTestDirectory(graphName, test, testMethodName); 72 | File testDataRootFile = Paths.get(directory).toFile(); 73 | testDataRootFile.mkdirs(); 74 | 75 | try { 76 | if (testDataRootFile.exists()) { 77 | wipeOut(testDataRootFile); 78 | } 79 | } catch (Exception ex) { 80 | System.out.println("SETUP FAILED!!!"); 81 | ex.printStackTrace(); 82 | } 83 | 84 | String testDataRoot = testDataRootFile.getPath(); 85 | 86 | return new HashMap() { 87 | { 88 | put(Graph.GRAPH, BitsyAutoReloadingGraph.class.getName()); 89 | put(BitsyGraph.DB_PATH_KEY, testDataRoot); 90 | put(BitsyGraph.CREATE_DIR_IF_MISSING_KEY, true); 91 | put(BitsyGraph.DEFAULT_ISOLATION_LEVEL_KEY, BitsyIsolationLevel.READ_COMMITTED.toString()); 92 | put(BitsyGraph.VERTEX_INDICES_KEY, "name, foo"); 93 | } 94 | }; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/test/java/com/lambdazen/bitsy/structure/HasLabelTest.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.structure; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import com.lambdazen.bitsy.BitsyGraph; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import org.apache.tinkerpop.gremlin.process.traversal.P; 9 | import org.apache.tinkerpop.gremlin.structure.Graph; 10 | import org.apache.tinkerpop.gremlin.structure.T; 11 | import org.junit.Test; 12 | 13 | public class HasLabelTest { 14 | @Test 15 | public void bitsyPredicateTest() { 16 | Graph g = new BitsyGraph(); 17 | 18 | final List props1 = new ArrayList<>(); 19 | props1.add(T.label); 20 | props1.add("vert1"); 21 | props1.add("p1"); 22 | props1.add(1); 23 | props1.add("p2"); 24 | props1.add("2"); 25 | g.addVertex(props1.toArray()); 26 | 27 | final List props2 = new ArrayList<>(); 28 | props2.add(T.label); 29 | props2.add("vert2"); 30 | props2.add("2p1"); 31 | props2.add(1); 32 | props2.add("2p2"); 33 | props2.add("2"); 34 | Object vert2 = g.addVertex(props2.toArray()); 35 | 36 | assertTrue(g.traversal().V().hasLabel(P.within("vert2")).next().equals(vert2)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/com/lambdazen/bitsy/util/CommittableFileLogTest.java: -------------------------------------------------------------------------------- 1 | package com.lambdazen.bitsy.util; 2 | 3 | import com.lambdazen.bitsy.store.FileBackedMemoryGraphStore; 4 | import java.io.BufferedReader; 5 | import java.io.File; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.nio.file.Files; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Random; 12 | import junit.framework.TestCase; 13 | 14 | public class CommittableFileLogTest extends TestCase { 15 | public CommittableFileLogTest() {} 16 | 17 | public void testRead() throws Exception { 18 | File tempFile = File.createTempFile("mobydick", ".txt"); 19 | 20 | InputStream is = getClass().getResourceAsStream("mobydick.txt"); 21 | assertNotNull(is); 22 | BufferedReader br = new BufferedReader(new InputStreamReader(is, FileBackedMemoryGraphStore.utf8)); 23 | 24 | CommittableFileLog cflWrite = new CommittableFileLog(tempFile.toPath(), false); 25 | cflWrite.openForOverwrite(1L); 26 | 27 | Random rand = new Random(); 28 | int expectedLines = -1; 29 | for (int run = 0; run < 10; run++) { 30 | List lines = new ArrayList(); 31 | String line = null; 32 | while ((line = br.readLine()) != null) { 33 | lines.add(line); 34 | if (run == 0) { 35 | cflWrite.append((line + '\n').getBytes(FileBackedMemoryGraphStore.utf8)); 36 | } 37 | } 38 | 39 | br.close(); 40 | is.close(); 41 | 42 | if (run == 0) { 43 | cflWrite.close(); 44 | } 45 | 46 | if (expectedLines != -1) { 47 | assertEquals(expectedLines, lines.size()); 48 | } 49 | 50 | System.out.println("Finished writing to " + tempFile); 51 | 52 | List nioLines = new ArrayList(); 53 | CommittableFileLog cfl = new CommittableFileLog(tempFile.toPath(), false); 54 | cfl.openForRead(); 55 | 56 | line = null; 57 | int byteCounter = 13; // Start with header 58 | int linesDeleted = 0; 59 | while ((line = cfl.readLine()) != null) { 60 | // System.out.println("Line: " + line); 61 | nioLines.add(line); 62 | int lineBytes = 1 + line.getBytes(FileBackedMemoryGraphStore.utf8).length; 63 | byteCounter += lineBytes; 64 | 65 | if (rand.nextDouble() < 0.1) { 66 | cfl.mark(); 67 | assertEquals(byteCounter, cfl.getMarkPosition()); 68 | linesDeleted = 0; 69 | } else if (rand.nextDouble() < 0.15) { 70 | cfl.mark(lineBytes); 71 | // System.out.println("Line bytes: " + lineBytes); 72 | assertEquals(byteCounter - lineBytes, cfl.getMarkPosition()); 73 | linesDeleted = 1; 74 | } else { 75 | linesDeleted++; 76 | } 77 | } 78 | 79 | assertEquals(lines.size(), nioLines.size()); 80 | expectedLines = lines.size() - linesDeleted; 81 | 82 | for (int i = 0; i < lines.size(); i++) { 83 | assertEquals(lines.get(i), nioLines.get(i)); 84 | } 85 | 86 | cfl.truncateAtMark(); 87 | cfl.close(); 88 | 89 | // For the next run -- use the temp file for reading 90 | is = Files.newInputStream(tempFile.toPath()); 91 | br = new BufferedReader(new InputStreamReader(is, FileBackedMemoryGraphStore.utf8)); 92 | 93 | // Skip the first line with the header 94 | br.readLine(); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/test/resources/gremlin-server/bitsy.properties: -------------------------------------------------------------------------------- 1 | # Sample configuration for Bitsy in Gremlin server 2 | # More details on tuning at https://github.com/lambdazen/bitsy/wiki/TuningBitsy.md 3 | gremlin.graph=com.lambdazen.bitsy.BitsyGraph 4 | 5 | # CHANGE THIS!! 6 | dbPath=/bitsy-db/ 7 | -------------------------------------------------------------------------------- /src/test/resources/gremlin-server/gremlin-server-bitsy.yaml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | host: localhost 19 | port: 8182 20 | scriptEvaluationTimeout: 30000 21 | graphs: { 22 | graph: conf/bitsy.properties} 23 | scriptEngines: { 24 | gremlin-groovy: { 25 | plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {}, 26 | com.lambdazen.bitsy.jsr223.BitsyGremlinPlugin: {}, 27 | org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]}, 28 | org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/empty-sample.groovy]}}}} 29 | serializers: 30 | - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { ioRegistries: [com.lambdazen.bitsy.BitsyIoRegistryV3d0] }} # application/vnd.gremlin-v3.0+gryo 31 | - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { serializeResultToString: true }} # application/vnd.gremlin-v3.0+gryo-stringd 32 | - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0, config: { ioRegistries: [com.lambdazen.bitsy.BitsyIoRegistryV3d0] }} # application/json 33 | metrics: { 34 | slf4jReporter: {enabled: true, interval: 180000}} 35 | strictTransactionManagement: false 36 | maxInitialLineLength: 4096 37 | maxHeaderSize: 8192 38 | maxChunkSize: 8192 39 | maxContentLength: 65536 40 | maxAccumulationBufferComponents: 1024 41 | resultIterationBatchSize: 64 42 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage1/eA.txt: -------------------------------------------------------------------------------- 1 | H=5#0021a643 2 | L=2#00237762 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage1/eB.txt: -------------------------------------------------------------------------------- 1 | H=6#0021a662 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage1/metaA.txt: -------------------------------------------------------------------------------- 1 | H=7#0021a681 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage1/metaB.txt: -------------------------------------------------------------------------------- 1 | H=8#0021a6a0 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage1/txA.txt: -------------------------------------------------------------------------------- 1 | H=9#0021a6bf 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage1/vA.txt: -------------------------------------------------------------------------------- 1 | H=3#0021a605 2 | L=2#00237762 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage1/vB.txt: -------------------------------------------------------------------------------- 1 | H=4#0021a624 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage2/eA.txt: -------------------------------------------------------------------------------- 1 | H=5#0021a643 2 | L=2#00237762 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage2/eB.txt: -------------------------------------------------------------------------------- 1 | H=6#0021a662 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage2/metaA.txt: -------------------------------------------------------------------------------- 1 | H=7#0021a681 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage2/metaB.txt: -------------------------------------------------------------------------------- 1 | H=8#0021a6a0 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage2/txA.txt: -------------------------------------------------------------------------------- 1 | H=9#0021a6bf 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage2/vA.txt: -------------------------------------------------------------------------------- 1 | H=3#0021a605 2 | L=2#00237762 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage2/vB.txt: -------------------------------------------------------------------------------- 1 | H=4#0021a624 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage3/eA.txt: -------------------------------------------------------------------------------- 1 | H=5#0021a643 2 | L=2#00237762 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage3/eB.txt: -------------------------------------------------------------------------------- 1 | H=6#0021a662 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage3/metaA.txt: -------------------------------------------------------------------------------- 1 | H=7#0021a681 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage3/metaB.txt: -------------------------------------------------------------------------------- 1 | H=8#0021a6a0 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage3/txA.txt: -------------------------------------------------------------------------------- 1 | H=9#0021a6bf 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage3/vA.txt: -------------------------------------------------------------------------------- 1 | H=3#0021a605 2 | V={"id":"4939a570-2fc3-49c4-a88c-8dbf995ca059","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#cbe9c5f7 3 | V={"id":"62c59bfe-954b-4a8d-b166-79b0394db432","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#b3f98903 4 | V={"id":"cae53026-792f-4758-8836-82ef253b758b","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#be0fdd1f 5 | V={"id":"0a423fea-ed66-4151-ac85-a16d28603099","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#46d8619c 6 | V={"id":"7441ee87-ed85-4c7b-a6b7-9ef828a8ae66","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#55891ca4 7 | V={"id":"9dea9b70-22a7-4d66-b543-763d39e74ac3","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#6901f89a 8 | V={"id":"bdf65c96-20b5-4418-9e27-2525e6ce9d64","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#be873f0c 9 | V={"id":"a6a428fe-b6d2-427a-8674-41735bed89a1","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#36cd8ba2 10 | V={"id":"5711a7cb-3025-4fad-8605-28d12a08e6a2","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#22c63a3b 11 | V={"id":"ff60fb22-f142-435c-a1e9-dad78968e99d","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#20e7c528 12 | V={"id":"f390e034-c346-44c4-a919-bc45c615e70d","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#1b0707a2 13 | V={"id":"6a27512d-9d8b-4190-87ec-cd4dc8f8964d","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#f5a3412b 14 | V={"id":"91957136-002a-47e4-8351-a45531dd374b","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#8d403c50 15 | V={"id":"0f73f23a-4113-48c1-ba3f-1596a2ed325e","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#581ea883 16 | V={"id":"893f6b99-7da4-4d2d-8660-b3025ef094d4","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#c97c97ce 17 | V={"id":"53ccbd4a-1071-4dd4-9797-cf9820d30f97","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#eadd8e95 18 | V={"id":"29b633c4-791d-4084-ac52-112bab10e8d0","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#79a97e91 19 | V={"id":"5e32e7c0-3097-4af3-8534-41b00e5c2075","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#abd83757 20 | V={"id":"061aa8c7-3d4d-42a9-bf2c-5b41bf7c3334","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#a7bb8ad4 21 | V={"id":"ef292fa8-42c6-4d7d-b664-be4ecdc9a334","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#d7092785 22 | V={"id":"202a0cc9-de3e-45c5-b09d-ee5dfbf9f446","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#ecb86525 23 | V={"id":"204ffb01-4a52-41a1-bf62-ac8573f069e6","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#3f13c007 24 | V={"id":"6fe4d83c-e4a4-4195-9618-8a9b8dcdeabe","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#4c17785c 25 | V={"id":"282e4ee9-15be-45de-be85-f7e5541b504f","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#0617b779 26 | V={"id":"10df704b-c692-4934-836b-05937ffefd4e","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#c0e9f439 27 | V={"id":"1bcbe6d7-6c47-447e-b268-30f45e91d569","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#e934a569 28 | V={"id":"0c73d06c-57f1-4bf2-9f26-3e5bfbd6fb5a","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#99a8d417 29 | V={"id":"a095ae24-9764-4f3a-8925-c2553c7bdf0a","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#e551cdc2 30 | V={"id":"da27b121-cf6f-4062-8320-4a73e5bd5579","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#acb3edd7 31 | V={"id":"a14c8381-b950-4cad-b4c9-c6e6e23de413","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#9a27f052 32 | V={"id":"b820a339-9a45-44b3-a40d-25347c50c1fb","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#d5f4c62e 33 | V={"id":"2bdd4ed5-e0be-4f73-8842-56aba2b0d788","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#ff1615c8 34 | V={"id":"82146009-6c33-433e-a28c-62f59abf6bdd","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#941e52e5 35 | V={"id":"7735542b-7a75-4262-b448-5195f286bac8","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#861cef0d 36 | V={"id":"acf127c3-39a7-4421-9131-8b4082c39a74","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#357e1fac 37 | V={"id":"3311ee22-4b88-4126-83c3-b6e989a47bc9","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#bc280b6b 38 | V={"id":"2352c173-6966-4269-9154-58e6b4ab9bb7","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#25bd859c 39 | V={"id":"056d15fa-0c9d-47c5-8339-18ab9a4b8a5d","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#d18c08b2 40 | V={"id":"3aee3a5b-4643-4e3e-82fe-f954ff2666af","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#2f3e9de3 41 | V={"id":"1d048031-14ef-4b52-9b71-630575186d88","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#0b664aca 42 | V={"id":"4c941fb7-da15-4e44-9722-5519f775f9c3","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#64eb0a30 43 | V={"id":"fead9f79-1b9d-45cc-8b8f-509704b94324","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#335049bd 44 | V={"id":"7baad6bf-f18a-4de0-a819-38d12a6d7063","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#760171e2 45 | V={"id":"477e49ab-4033-477b-b834-eb48ccf34341","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#23774854 46 | V={"id":"6e839a0e-886c-4ec6-8389-7a18e988b123","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#0738f1d9 47 | V={"id":"944b3f17-2d84-4e4f-afa4-15908f4c0841","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#cd0e4774 48 | V={"id":"114e05da-5a69-4ff5-87fb-997b1f03c991","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#f6692975 49 | V={"id":"5c0f9af6-ff49-4938-807d-63e4c9d1faa3","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#c63cc9d8 50 | V={"id":"92e0e9ae-e7c2-4a5b-b35d-efdfc296bc9e","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#0de7295f 51 | V={"id":"d80c8a3a-dd7d-4c85-9603-85ef5bb20c41","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#add1d828 52 | L=2#00237762 53 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage3/vB.txt: -------------------------------------------------------------------------------- 1 | H=4#0021a624 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage4/eA.txt: -------------------------------------------------------------------------------- 1 | H=5#0021a643 2 | L=2#00237762 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage4/eB.txt: -------------------------------------------------------------------------------- 1 | H=6#0021a662 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage4/metaA.txt: -------------------------------------------------------------------------------- 1 | H=7#0021a681 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage4/metaB.txt: -------------------------------------------------------------------------------- 1 | H=8#0021a6a0 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage4/txA.txt: -------------------------------------------------------------------------------- 1 | H=9#0021a6bf 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage4/vA.txt: -------------------------------------------------------------------------------- 1 | H=3#0021a605 2 | V={"id":"4939a570-2fc3-49c4-a88c-8dbf995ca059","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#cbe9c5f7 3 | V={"id":"62c59bfe-954b-4a8d-b166-79b0394db432","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#b3f98903 4 | V={"id":"cae53026-792f-4758-8836-82ef253b758b","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#be0fdd1f 5 | V={"id":"0a423fea-ed66-4151-ac85-a16d28603099","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#46d8619c 6 | V={"id":"7441ee87-ed85-4c7b-a6b7-9ef828a8ae66","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#55891ca4 7 | V={"id":"9dea9b70-22a7-4d66-b543-763d39e74ac3","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#6901f89a 8 | V={"id":"bdf65c96-20b5-4418-9e27-2525e6ce9d64","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#be873f0c 9 | V={"id":"a6a428fe-b6d2-427a-8674-41735bed89a1","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#36cd8ba2 10 | V={"id":"5711a7cb-3025-4fad-8605-28d12a08e6a2","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#22c63a3b 11 | V={"id":"ff60fb22-f142-435c-a1e9-dad78968e99d","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#20e7c528 12 | V={"id":"f390e034-c346-44c4-a919-bc45c615e70d","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#1b0707a2 13 | V={"id":"6a27512d-9d8b-4190-87ec-cd4dc8f8964d","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#f5a3412b 14 | V={"id":"91957136-002a-47e4-8351-a45531dd374b","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#8d403c50 15 | V={"id":"0f73f23a-4113-48c1-ba3f-1596a2ed325e","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#581ea883 16 | V={"id":"893f6b99-7da4-4d2d-8660-b3025ef094d4","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#c97c97ce 17 | V={"id":"53ccbd4a-1071-4dd4-9797-cf9820d30f97","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#eadd8e95 18 | V={"id":"29b633c4-791d-4084-ac52-112bab10e8d0","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#79a97e91 19 | V={"id":"5e32e7c0-3097-4af3-8534-41b00e5c2075","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#abd83757 20 | V={"id":"061aa8c7-3d4d-42a9-bf2c-5b41bf7c3334","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#a7bb8ad4 21 | V={"id":"ef292fa8-42c6-4d7d-b664-be4ecdc9a334","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#d7092785 22 | V={"id":"202a0cc9-de3e-45c5-b09d-ee5dfbf9f446","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#ecb86525 23 | V={"id":"204ffb01-4a52-41a1-bf62-ac8573f069e6","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#3f13c007 24 | V={"id":"6fe4d83c-e4a4-4195-9618-8a9b8dcdeabe","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#4c17785c 25 | V={"id":"282e4ee9-15be-45de-be85-f7e5541b504f","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#0617b779 26 | V={"id":"10df704b-c692-4934-836b-05937ffefd4e","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#c0e9f439 27 | L=2#00237762 28 | V={"id":"1bcbe6d7-6c47-447e-b268-30f45e91d569","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#e934a569 29 | V={"id":"0c73d06c-57f1-4bf2-9f26-3e5bfbd6fb5a","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#99a8d417 30 | V={"id":"a095ae24-9764-4f3a-8925-c2553c7bdf0a","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#e551cdc2 31 | V={"id":"da27b121-cf6f-4062-8320-4a73e5bd5579","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#acb3edd7 32 | V={"id":"a14c8381-b950-4cad-b4c9-c6e6e23de413","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#9a27f052 33 | V={"id":"b820a339-9a45-44b3-a40d-25347c50c1fb","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#d5f4c62e 34 | V={"id":"2bdd4ed5-e0be-4f73-8842-56aba2b0d788","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#ff1615c8 35 | V={"id":"82146009-6c33-433e-a28c-62f59abf6bdd","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#941e52e5 36 | V={"id":"7735542b-7a75-4262-b448-5195f286bac8","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#861cef0d 37 | V={"id":"acf127c3-39a7-4421-9131-8b4082c39a74","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#357e1fac 38 | V={"id":"3311ee22-4b88-4126-83c3-b6e989a47bc9","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#bc280b6b 39 | V={"id":"2352c173-6966-4269-9154-58e6b4ab9bb7","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#25bd859c 40 | V={"id":"056d15fa-0c9d-47c5-8339-18ab9a4b8a5d","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#d18c08b2 41 | V={"id":"3aee3a5b-4643-4e3e-82fe-f954ff2666af","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#2f3e9de3 42 | V={"id":"1d048031-14ef-4b52-9b71-630575186d88","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#0b664aca 43 | V={"id":"4c941fb7-da15-4e44-9722-5519f775f9c3","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#64eb0a30 44 | V={"id":"fead9f79-1b9d-45cc-8b8f-509704b94324","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#335049bd 45 | V={"id":"7baad6bf-f18a-4de0-a819-38d12a6d7063","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#760171e2 46 | V={"id":"477e49ab-4033-477b-b834-eb48ccf34341","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#23774854 47 | V={"id":"6e839a0e-886c-4ec6-8389-7a18e988b123","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#0738f1d9 48 | V={"id":"944b3f17-2d84-4e4f-afa4-15908f4c0841","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#cd0e4774 49 | V={"id":"114e05da-5a69-4ff5-87fb-997b1f03c991","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#f6692975 50 | V={"id":"5c0f9af6-ff49-4938-807d-63e4c9d1faa3","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#c63cc9d8 51 | V={"id":"92e0e9ae-e7c2-4a5b-b35d-efdfc296bc9e","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#0de7295f 52 | V={"id":"d80c8a3a-dd7d-4c85-9603-85ef5bb20c41","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#add1d828 53 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage4/vB.txt: -------------------------------------------------------------------------------- 1 | H=4#0021a624 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage5/eA.txt: -------------------------------------------------------------------------------- 1 | H=5#0021a643 2 | L=2#00237762 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage5/eB.txt: -------------------------------------------------------------------------------- 1 | H=6#0021a662 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage5/metaA.txt: -------------------------------------------------------------------------------- 1 | H=7#0021a681 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage5/metaB.txt: -------------------------------------------------------------------------------- 1 | H=8#0021a6a0 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage5/txA.txt: -------------------------------------------------------------------------------- 1 | H=9#0021a6bf 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage5/vA.txt: -------------------------------------------------------------------------------- 1 | H=3#0021a605 2 | V={"id":"4939a570-2fc3-49c4-a88c-8dbf995ca059","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#cbe9c5f7 3 | V={"id":"62c59bfe-954b-4a8d-b166-79b0394db432","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#b3f98903 4 | V={"id":"cae53026-792f-4758-8836-82ef253b758b","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#be0fdd1f 5 | V={"id":"0a423fea-ed66-4151-ac85-a16d28603099","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#46d8619c 6 | V={"id":"7441ee87-ed85-4c7b-a6b7-9ef828a8ae66","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#55891ca4 7 | V={"id":"9dea9b70-22a7-4d66-b543-763d39e74ac3","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#6901f89a 8 | V={"id":"bdf65c96-20b5-4418-9e27-2525e6ce9d64","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#be873f0c 9 | V={"id":"a6a428fe-b6d2-427a-8674-41735bed89a1","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#36cd8ba2 10 | V={"id":"5711a7cb-3025-4fad-8605-28d12a08e6a2","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#22c63a3b 11 | V={"id":"ff60fb22-f142-435c-a1e9-dad78968e99d","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#20e7c528 12 | V={"id":"f390e034-c346-44c4-a919-bc45c615e70d","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#1b0707a2 13 | V={"id":"6a27512d-9d8b-4190-87ec-cd4dc8f8964d","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#f5a3412b 14 | V={"id":"91957136-002a-47e4-8351-a45531dd374b","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#8d403c50 15 | V={"id":"0f73f23a-4113-48c1-ba3f-1596a2ed325e","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#581ea883 16 | V={"id":"893f6b99-7da4-4d2d-8660-b3025ef094d4","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#c97c97ce 17 | V={"id":"53ccbd4a-1071-4dd4-9797-cf9820d30f97","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#eadd8e95 18 | V={"id":"29b633c4-791d-4084-ac52-112bab10e8d0","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#79a97e91 19 | V={"id":"5e32e7c0-3097-4af3-8534-41b00e5c2075","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#abd83757 20 | V={"id":"061aa8c7-3d4d-42a9-bf2c-5b41bf7c3334","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#a7bb8ad4 21 | V={"id":"ef292fa8-42c6-4d7d-b664-be4ecdc9a334","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#d7092785 22 | V={"id":"202a0cc9-de3e-45c5-b09d-ee5dfbf9f446","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#ecb86525 23 | V={"id":"204ffb01-4a52-41a1-bf62-ac8573f069e6","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#3f13c007 24 | V={"id":"6fe4d83c-e4a4-4195-9618-8a9b8dcdeabe","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#4c17785c 25 | V={"id":"282e4ee9-15be-45de-be85-f7e5541b504f","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#0617b779 26 | V={"id":"10df704b-c692-4934-836b-05937ffefd4e","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#c0e9f439 27 | L=2#00237762 28 | V={"id":"1bcbe6d7-6c47-447e-b268-30f45e91d569","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#e934a569 29 | V={"id":"0c73d06c-57f1-4bf2-9f26-3e5bfbd6fb5a","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#99a8d417 30 | V={"id":"a095ae24-9764-4f3a-8925-c2553c7bdf0a","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#e551cdc2 31 | V={"id":"da27b121-cf6f-4062-8320-4a73e5bd5579","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#acb3edd7 32 | V={"id":"a14c8381-b950-4cad-b4c9-c6e6e23de413","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#9a27f052 33 | V={"id":"b820a339-9a45-44b3-a40d-25347c50c1fb","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#d5f4c62e 34 | V={"id":"2bdd4ed5-e0be-4f73-8842-56aba2b0d788","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#ff1615c8 35 | V={"id":"82146009-6c33-433e-a28c-62f59abf6bdd","v":1,"s":"M","p":{"foo":"something \n multi-line"}}#941e52e5 36 | V={"id":"7735542b-7a75-4262-b448-5195f286bac8","v":1ሰማይ አይታረስ ንጉሥ አይከሰስ። 37 | ብላ ካለኝ እንደአባቴ በቆመጠኝ። 38 | ጌጥ ያለቤቱ ቁምጥና ነው። 39 | ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው። 40 | የአፍ ወለምታ በቅቤ አይታሽም። 41 | አይጥ በበላ ዳዋ ተመታ። 42 | ሲተረጉሙ ይደረግሙ። 43 | ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል። 44 | ድር ቢያብር አንበሳ ያስር። 45 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage5/vB.txt: -------------------------------------------------------------------------------- 1 | H=4#0021a624 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage6/metaA.txt: -------------------------------------------------------------------------------- 1 | H=7#0021a681 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage6/metaB.txt: -------------------------------------------------------------------------------- 1 | H=8#0021a6a0 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage6/txA.txt: -------------------------------------------------------------------------------- 1 | H=9#0021a6bf 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage6/txB.txt: -------------------------------------------------------------------------------- 1 | H=10#041314cf 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage7/metaA.txt: -------------------------------------------------------------------------------- 1 | H=7#0021a681 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage7/metaB.txt: -------------------------------------------------------------------------------- 1 | H=8#0021a6a0 2 | M=1.5#86d7bafb 3 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage7/txA.txt: -------------------------------------------------------------------------------- 1 | H=9#0021a6bf 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage7/txB.txt: -------------------------------------------------------------------------------- 1 | H=10#041314cf 2 | -------------------------------------------------------------------------------- /src/test/resources/recovery/stage7/vA.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdazen/bitsy/e1e27d7bf6c940750eaeef92ff6a92aa24bc9688/src/test/resources/recovery/stage7/vA.txt --------------------------------------------------------------------------------