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>
|
||||
<modules>
|
||||
<module>server</module>
|
||||
<module>kleppmanntree</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
|
||||
Reference in New Issue
Block a user