mirror of
https://github.com/usatiuk/dhfs.git
synced 2025-10-29 04:57:48 +01:00
proper tree merging beginnings
This commit is contained in:
35
dhfs-parent/kleppmanntree/pom.xml
Normal file
35
dhfs-parent/kleppmanntree/pom.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.usatiuk.dhfs</groupId>
|
||||||
|
<artifactId>parent</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>com.usatiuk</groupId>
|
||||||
|
<artifactId>kleppmanntree</artifactId>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>21</maven.compiler.source>
|
||||||
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.32</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<version>5.9.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
1
dhfs-parent/kleppmanntree/src/lombok.config
Normal file
1
dhfs-parent/kleppmanntree/src/lombok.config
Normal file
@@ -0,0 +1 @@
|
|||||||
|
lombok.accessors.prefix += _
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
public interface Clock<TimestampT extends Comparable<TimestampT>> {
|
||||||
|
TimestampT getTimestamp();
|
||||||
|
|
||||||
|
void updateTimestamp(TimestampT receivedTimestamp);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
public record CombinedTimestamp<TimestampT extends Comparable<TimestampT>, PeerIdT extends Comparable<PeerIdT>>
|
||||||
|
(TimestampT timestamp, PeerIdT nodeId) implements Comparable<CombinedTimestamp<TimestampT, PeerIdT>> {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(CombinedTimestamp<TimestampT, PeerIdT> o) {
|
||||||
|
return Comparator.comparing((CombinedTimestamp<TimestampT, PeerIdT> t) -> t.timestamp)
|
||||||
|
.thenComparing((CombinedTimestamp<TimestampT, PeerIdT> t) -> t.nodeId)
|
||||||
|
.compare(this, o);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class KleppmannTree<TimestampT extends Comparable<TimestampT>, PeerIdT extends Comparable<PeerIdT>, NameT, MetaT extends NodeMeta<NameT>, NodeIdT, WrapperT extends TreeNodeWrapper<NameT, MetaT, NodeIdT>> {
|
||||||
|
private final StorageInterface<TimestampT, PeerIdT, NameT, MetaT, NodeIdT, WrapperT> _storage;
|
||||||
|
private final PeerInterface<PeerIdT> _peers;
|
||||||
|
private final Clock<TimestampT> _clock;
|
||||||
|
|
||||||
|
public KleppmannTree(StorageInterface<TimestampT, PeerIdT, NameT, MetaT, NodeIdT, WrapperT> storage,
|
||||||
|
PeerInterface<PeerIdT> peers,
|
||||||
|
Clock<TimestampT> clock) {
|
||||||
|
_storage = storage;
|
||||||
|
_peers = peers;
|
||||||
|
_clock = clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public NodeIdT traverse(NodeIdT fromId, List<NameT> names) {
|
||||||
|
if (names.isEmpty()) return fromId;
|
||||||
|
|
||||||
|
var from = _storage.getById(fromId);
|
||||||
|
from.rLock();
|
||||||
|
NodeIdT childId;
|
||||||
|
try {
|
||||||
|
childId = from.getNode().getChildren().get(names.getFirst());
|
||||||
|
} finally {
|
||||||
|
from.rwUnlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childId == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return traverse(childId, names.subList(1, names.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeIdT traverse(List<NameT> names) {
|
||||||
|
return traverse(_storage.getRootId(), names);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void undoOp(LogOpMove<TimestampT, PeerIdT, NameT, MetaT, NodeIdT> op) {
|
||||||
|
if (op.oldInfo() != null) {
|
||||||
|
var node = _storage.getById(op.op().childId());
|
||||||
|
var oldParent = _storage.getById(op.oldInfo().oldParent());
|
||||||
|
var curParent = _storage.getById(op.op().newParentId());
|
||||||
|
curParent.rwLock();
|
||||||
|
oldParent.rwLock();
|
||||||
|
node.rwLock();
|
||||||
|
try {
|
||||||
|
curParent.getNode().getChildren().remove(node.getNode().getMeta().getName());
|
||||||
|
node.getNode().setMeta(op.oldInfo().oldMeta());
|
||||||
|
node.getNode().setParent(oldParent.getNode().getId());
|
||||||
|
oldParent.getNode().getChildren().put(node.getNode().getMeta().getName(), node.getNode().getId());
|
||||||
|
} finally {
|
||||||
|
node.rwUnlock();
|
||||||
|
oldParent.rwUnlock();
|
||||||
|
curParent.rwUnlock();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var node = _storage.getById(op.op().childId());
|
||||||
|
var curParent = _storage.getById(op.op().newParentId());
|
||||||
|
curParent.rwLock();
|
||||||
|
node.rwLock();
|
||||||
|
try {
|
||||||
|
curParent.getNode().getChildren().remove(node.getNode().getMeta().getName());
|
||||||
|
_storage.removeNode(node.getNode().getId());
|
||||||
|
} finally {
|
||||||
|
node.rwUnlock();
|
||||||
|
curParent.rwUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void redoOp(Map.Entry<CombinedTimestamp<TimestampT, PeerIdT>, LogOpMove<TimestampT, PeerIdT, NameT, MetaT, NodeIdT>> entry) {
|
||||||
|
entry.setValue(doOp(entry.getValue().op()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyOp(OpMove<TimestampT, PeerIdT, NameT, MetaT, NodeIdT> op) {
|
||||||
|
_clock.updateTimestamp(op.timestamp().timestamp());
|
||||||
|
var log = _storage.getLog();
|
||||||
|
if (log.isEmpty()) {
|
||||||
|
log.put(op.timestamp(), doOp(op));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var cmp = op.timestamp().compareTo(log.lastEntry().getKey());
|
||||||
|
assert cmp != 0;
|
||||||
|
if (cmp < 0) {
|
||||||
|
_storage.globalLock();
|
||||||
|
try {
|
||||||
|
var toUndo = log.tailMap(op.timestamp(), false);
|
||||||
|
for (var entry : toUndo.reversed().entrySet()) {
|
||||||
|
undoOp(entry.getValue());
|
||||||
|
}
|
||||||
|
log.put(op.timestamp(), doOp(op));
|
||||||
|
for (var entry : toUndo.entrySet()) {
|
||||||
|
redoOp(entry);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_storage.globalUnlock();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.put(op.timestamp(), doOp(op));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LogOpMove<TimestampT, PeerIdT, NameT, MetaT, NodeIdT> doOp(OpMove<TimestampT, PeerIdT, NameT, MetaT, NodeIdT> op) {
|
||||||
|
var node = _storage.getById(op.childId());
|
||||||
|
if (node == null) {
|
||||||
|
node = _storage.createNewNode(op.childId());
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldParent = node.getNode().getParent() != null ? _storage.getById(node.getNode().getParent()) : null;
|
||||||
|
var newParent = _storage.getById(op.newParentId());
|
||||||
|
|
||||||
|
if (newParent == null) {
|
||||||
|
throw new IllegalArgumentException("New parent not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldParent == null) {
|
||||||
|
newParent.rwLock();
|
||||||
|
try {
|
||||||
|
node.rwLock();
|
||||||
|
try {
|
||||||
|
node.getNode().setMeta(op.newMeta());
|
||||||
|
node.getNode().setParent(op.newParentId());
|
||||||
|
newParent.getNode().getChildren().put(node.getNode().getMeta().getName(), node.getNode().getId());
|
||||||
|
return new LogOpMove<>(null, op);
|
||||||
|
} finally {
|
||||||
|
node.rwUnlock();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
newParent.rwUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME:
|
||||||
|
_storage.globalLock();
|
||||||
|
try {
|
||||||
|
if (op.childId() == op.newParentId() || isAncestor(op.childId(), op.newParentId()))
|
||||||
|
return new LogOpMove<>(null, op);
|
||||||
|
|
||||||
|
newParent.rwLock();
|
||||||
|
oldParent.rwLock();
|
||||||
|
node.rwLock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
oldParent.getNode().getChildren().remove(node.getNode().getMeta().getName());
|
||||||
|
node.getNode().setMeta(op.newMeta());
|
||||||
|
node.getNode().setParent(newParent.getNode().getId());
|
||||||
|
newParent.getNode().getChildren().put(op.newMeta().getName(), node.getNode().getId());
|
||||||
|
return new LogOpMove<>(new LogOpMoveOld<>(oldParent.getNode().getId(), node.getNode().getMeta()), op);
|
||||||
|
} finally {
|
||||||
|
node.rwUnlock();
|
||||||
|
oldParent.rwUnlock();
|
||||||
|
newParent.rwUnlock();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
_storage.globalUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAncestor(NodeIdT child, NodeIdT parent) {
|
||||||
|
var node = _storage.getById(parent);
|
||||||
|
NodeIdT curParent = null;
|
||||||
|
while ((curParent = node.getNode().getParent()) != null) {
|
||||||
|
if (Objects.equals(child, curParent)) return true;
|
||||||
|
node = _storage.getById(curParent);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
public record LogOpMove<TimestampT extends Comparable<TimestampT>, PeerIdT extends Comparable<PeerIdT>, NameT, MetaT extends NodeMeta<NameT>, NodeIdT>
|
||||||
|
(LogOpMoveOld<NameT, MetaT, NodeIdT> oldInfo,
|
||||||
|
OpMove<TimestampT, PeerIdT, NameT, MetaT, NodeIdT> op) {}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
public record LogOpMoveOld<NameT, MetaT extends NodeMeta<NameT>, NodeIdT>
|
||||||
|
(NodeIdT oldParent, MetaT oldMeta) {}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
public interface NodeMeta<NameT> {
|
||||||
|
public NameT getName();
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
public record OpMove<TimestampT extends Comparable<TimestampT>, PeerIdT extends Comparable<PeerIdT>, NameT, MetaT extends NodeMeta<NameT>, NodeIdT>
|
||||||
|
(CombinedTimestamp<TimestampT, PeerIdT> timestamp, NodeIdT newParentId, MetaT newMeta, NodeIdT childId) {}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public interface PeerInterface<PeerIdT extends Comparable<PeerIdT>> {
|
||||||
|
public PeerIdT getSelfId();
|
||||||
|
public Collection<PeerIdT> getAllPeers();
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.NavigableMap;
|
||||||
|
|
||||||
|
public interface StorageInterface<
|
||||||
|
TimestampT extends Comparable<TimestampT>,
|
||||||
|
PeerIdT extends Comparable<PeerIdT>,
|
||||||
|
NameT,
|
||||||
|
MetaT extends NodeMeta<NameT>,
|
||||||
|
NodeIdT,
|
||||||
|
WrapperT extends TreeNodeWrapper<NameT, MetaT, NodeIdT>> {
|
||||||
|
NodeIdT getRootId();
|
||||||
|
|
||||||
|
NodeIdT getTrashId();
|
||||||
|
|
||||||
|
NodeIdT getNewNodeId();
|
||||||
|
|
||||||
|
WrapperT getById(NodeIdT id);
|
||||||
|
|
||||||
|
WrapperT createNewNode(NodeIdT id);
|
||||||
|
|
||||||
|
void removeNode(NodeIdT id);
|
||||||
|
|
||||||
|
void lockSet(Collection<WrapperT> nodes);
|
||||||
|
|
||||||
|
// It is expected that the map allows concurrent additions at the end
|
||||||
|
NavigableMap<CombinedTimestamp<TimestampT, PeerIdT>, LogOpMove<TimestampT, PeerIdT, NameT, MetaT, NodeIdT>> getLog();
|
||||||
|
|
||||||
|
// Locks all the objects from being changed
|
||||||
|
void globalLock();
|
||||||
|
|
||||||
|
void globalUnlock();
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class TreeNode<NameT, MetaT extends NodeMeta<NameT>, NodeIdT> {
|
||||||
|
private NodeIdT _parent = null;
|
||||||
|
private final NodeIdT _id;
|
||||||
|
private MetaT _meta = null;
|
||||||
|
private Map<NameT, NodeIdT> _children = new HashMap<>();
|
||||||
|
|
||||||
|
public TreeNode(NodeIdT id) {_id = id;}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
public interface TreeNodeWrapper<NameT, MetaT extends NodeMeta<NameT>, NodeIdT> {
|
||||||
|
void rLock();
|
||||||
|
|
||||||
|
void rUnlock();
|
||||||
|
|
||||||
|
void rwLock();
|
||||||
|
|
||||||
|
void rwUnlock();
|
||||||
|
|
||||||
|
TreeNode<NameT, MetaT, NodeIdT> getNode();
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class KleppmanTreeSimpleTest {
|
||||||
|
private final TestNode testNode1 = new TestNode(1);
|
||||||
|
private final TestNode testNode2 = new TestNode(2);
|
||||||
|
private final TestNode testNode3 = new TestNode(3);
|
||||||
|
private final TestNode testNode4 = new TestNode(4);
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void circularTest() {
|
||||||
|
var d1id = testNode1._storageInterface.getNewNodeId();
|
||||||
|
var d2id = testNode2._storageInterface.getNewNodeId();
|
||||||
|
var op1 = new OpMove<>(new CombinedTimestamp<>(testNode1._clock.getTimestamp(), 1L),
|
||||||
|
testNode1._storageInterface.getRootId(),
|
||||||
|
new TestNodeMetaDir("Test1"),
|
||||||
|
d1id);
|
||||||
|
var op2 = new OpMove<>(new CombinedTimestamp<>(testNode2._clock.getTimestamp(), 2L),
|
||||||
|
testNode2._storageInterface.getRootId(),
|
||||||
|
new TestNodeMetaDir("Test2"),
|
||||||
|
d2id);
|
||||||
|
testNode1._tree.applyOp(op1);
|
||||||
|
testNode2._tree.applyOp(op2);
|
||||||
|
testNode1._tree.applyOp(op2);
|
||||||
|
testNode2._tree.applyOp(op1);
|
||||||
|
|
||||||
|
Assertions.assertEquals(d1id, testNode1._tree.traverse(List.of("Test1")));
|
||||||
|
Assertions.assertEquals(d2id, testNode1._tree.traverse(List.of("Test2")));
|
||||||
|
Assertions.assertEquals(d1id, testNode2._tree.traverse(List.of("Test1")));
|
||||||
|
Assertions.assertEquals(d2id, testNode2._tree.traverse(List.of("Test2")));
|
||||||
|
|
||||||
|
Assertions.assertIterableEquals(List.of("Test1", "Test2"), testNode1._storageInterface.getById(testNode2._storageInterface.getRootId()).getNode().getChildren().keySet());
|
||||||
|
Assertions.assertIterableEquals(List.of("Test1", "Test2"), testNode2._storageInterface.getById(testNode2._storageInterface.getRootId()).getNode().getChildren().keySet());
|
||||||
|
|
||||||
|
var cop1 = new OpMove<>(new CombinedTimestamp<>(testNode1._clock.getTimestamp(), 1L),
|
||||||
|
d1id,
|
||||||
|
new TestNodeMetaDir("Test2"),
|
||||||
|
d2id);
|
||||||
|
testNode1._tree.applyOp(cop1);
|
||||||
|
Assertions.assertEquals(d1id, testNode1._tree.traverse(List.of("Test1")));
|
||||||
|
Assertions.assertEquals(d2id, testNode1._tree.traverse(List.of("Test1", "Test2")));
|
||||||
|
Assertions.assertIterableEquals(List.of("Test1"), testNode1._storageInterface.getById(testNode2._storageInterface.getRootId()).getNode().getChildren().keySet());
|
||||||
|
|
||||||
|
var cop2 = new OpMove<>(new CombinedTimestamp<>(testNode2._clock.getTimestamp(), 2L),
|
||||||
|
d2id,
|
||||||
|
new TestNodeMetaDir("Test1"),
|
||||||
|
d1id);
|
||||||
|
testNode2._tree.applyOp(cop2);
|
||||||
|
Assertions.assertIterableEquals(List.of("Test2"), testNode2._storageInterface.getById(testNode2._storageInterface.getRootId()).getNode().getChildren().keySet());
|
||||||
|
Assertions.assertEquals(d2id, testNode2._tree.traverse(List.of("Test2")));
|
||||||
|
Assertions.assertEquals(d1id, testNode2._tree.traverse(List.of("Test2", "Test1")));
|
||||||
|
|
||||||
|
testNode1._tree.applyOp(cop2);
|
||||||
|
testNode2._tree.applyOp(cop1);
|
||||||
|
// First node wins as it has smaller timestamp
|
||||||
|
Assertions.assertIterableEquals(List.of("Test1"), testNode1._storageInterface.getById(testNode2._storageInterface.getRootId()).getNode().getChildren().keySet());
|
||||||
|
Assertions.assertIterableEquals(List.of("Test2"), testNode1._storageInterface.getById(d1id).getNode().getChildren().keySet());
|
||||||
|
Assertions.assertEquals(d1id, testNode1._tree.traverse(List.of("Test1")));
|
||||||
|
Assertions.assertEquals(d2id, testNode1._tree.traverse(List.of("Test1", "Test2")));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
public class TestClock implements Clock<Long> {
|
||||||
|
private long max = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getTimestamp() {
|
||||||
|
return max++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateTimestamp(Long receivedTimestamp) {
|
||||||
|
max = Math.max(max, receivedTimestamp) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
public class TestNode {
|
||||||
|
protected final long _id;
|
||||||
|
|
||||||
|
protected final TestClock _clock;
|
||||||
|
protected final TestPeerInterface _peerInterface;
|
||||||
|
protected final TestStorageInterface _storageInterface;
|
||||||
|
protected final KleppmannTree<Long, Long, String, TestNodeMetaDir, Long, TestNodeWrapper> _tree;
|
||||||
|
|
||||||
|
public TestNode(long id) {
|
||||||
|
_id = id;
|
||||||
|
_clock = new TestClock();
|
||||||
|
_peerInterface = new TestPeerInterface(_id);
|
||||||
|
_storageInterface = new TestStorageInterface(_id);
|
||||||
|
_tree = new KleppmannTree<>(_storageInterface, _peerInterface, _clock);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
public class TestNodeMetaDir implements NodeMeta<String> {
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public TestNodeMetaDir(String name) {this.name = name;}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public class TestNodeMetaFile implements NodeMeta<String> {
|
||||||
|
private final String name;
|
||||||
|
@Getter
|
||||||
|
private final long inode;
|
||||||
|
|
||||||
|
public TestNodeMetaFile(String name, long inode) {
|
||||||
|
this.name = name;
|
||||||
|
this.inode = inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
public class TestNodeWrapper implements TreeNodeWrapper<String, TestNodeMetaDir, Long> {
|
||||||
|
private final TreeNode<String, TestNodeMetaDir, Long> _backingNode;
|
||||||
|
|
||||||
|
public TestNodeWrapper(TreeNode<String, TestNodeMetaDir, Long> backingNode) {_backingNode = backingNode;}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rLock() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rUnlock() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rwLock() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rwUnlock() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TreeNode<String, TestNodeMetaDir, Long> getNode() {
|
||||||
|
return _backingNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TestPeerInterface implements PeerInterface<Long> {
|
||||||
|
private final long selfId;
|
||||||
|
|
||||||
|
public TestPeerInterface(long selfId) {this.selfId = selfId;}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getSelfId() {
|
||||||
|
return selfId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Long> getAllPeers() {
|
||||||
|
return List.of(1L, 2L, 3L, 4L);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.usatiuk.kleppmanntree;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class TestStorageInterface implements StorageInterface<Long, Long, String, TestNodeMetaDir, Long, TestNodeWrapper> {
|
||||||
|
private long _curId = 1;
|
||||||
|
private final long _peerId;
|
||||||
|
|
||||||
|
private final Map<Long, TreeNode<String, TestNodeMetaDir, Long>> _nodes = new HashMap<>();
|
||||||
|
private final NavigableMap<CombinedTimestamp<Long, Long>, LogOpMove<Long, Long, String, TestNodeMetaDir, Long>> _log = new TreeMap<>();
|
||||||
|
|
||||||
|
public TestStorageInterface(long peerId) {
|
||||||
|
_peerId = peerId;
|
||||||
|
_nodes.put(getRootId(), new TreeNode<>(getRootId()));
|
||||||
|
_nodes.put(getTrashId(), new TreeNode<>(getTrashId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getRootId() {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getTrashId() {
|
||||||
|
return -1L;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getNewNodeId() {
|
||||||
|
return _curId++ | _peerId << 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestNodeWrapper getById(Long id) {
|
||||||
|
var node = _nodes.get(id);
|
||||||
|
return node == null ? null : new TestNodeWrapper(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestNodeWrapper createNewNode(Long id) {
|
||||||
|
if (!_nodes.containsKey(id)) {
|
||||||
|
var newNode = new TreeNode<String, TestNodeMetaDir, Long>(id);
|
||||||
|
_nodes.put(id, newNode);
|
||||||
|
return new TestNodeWrapper(newNode);
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Node with id " + id + " already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeNode(Long id) {
|
||||||
|
if (!_nodes.containsKey(id))
|
||||||
|
throw new IllegalStateException("Node with id " + id + " doesn't exist");
|
||||||
|
_nodes.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void lockSet(Collection<TestNodeWrapper> nodes) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NavigableMap<CombinedTimestamp<Long, Long>, LogOpMove<Long, Long, String, TestNodeMetaDir, Long>> getLog() {
|
||||||
|
return _log;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void globalLock() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void globalUnlock() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<modules>
|
<modules>
|
||||||
<module>server</module>
|
<module>server</module>
|
||||||
|
<module>kleppmanntree</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
Reference in New Issue
Block a user