Skip to content

Commit

Permalink
Using a BTree for the cache engine instead of a LinkedHashMap. (#57) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelsilverioit authored Mar 16, 2024
1 parent 8d860e3 commit cb0358e
Show file tree
Hide file tree
Showing 11 changed files with 337 additions and 74 deletions.
19 changes: 19 additions & 0 deletions stundb/api/src/main/java/com/stundb/api/btree/BTree.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.stundb.api.btree;

import java.util.Collection;
import java.util.Optional;

public interface BTree<K extends Comparable<K>, V> {

void putIfAbsent(K key, V value);

Optional<Node<K, V>> find(K key);

Collection<Node<K, V>> findAll();

void remove(K key);

void clear();

Integer size();
}
176 changes: 176 additions & 0 deletions stundb/api/src/main/java/com/stundb/api/btree/BTreeImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package com.stundb.api.btree;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

public class BTreeImpl<K extends Comparable<K>, V> implements BTree<K, V> {

private final AtomicInteger size = new AtomicInteger(0);
private Node<K, V> root;

@Override
public void putIfAbsent(K key, V value) {
if (root == null) {
root = new Node<>(key, value);
size.getAndIncrement();
return;
}

var node = find(key, root);
if (node.isPresent()) {
return;
}

var target = root;

while (target != null) {
if (key.compareTo(target.getKey()) > 0) {
if (target.getRight() == null) {
target.setRight(new Node<>(key, value));
size.getAndIncrement();
return;
}

target = target.getRight();
continue;
}

if (target.getLeft() == null) {
target.setLeft(new Node<>(key, value));
size.getAndIncrement();
return;
}

target = target.getLeft();
}
}

@Override
public Optional<Node<K, V>> find(K key) {
return find(key, root);
}

@Override
public Collection<Node<K, V>> findAll() {
return getNodeValue(root);
}

@Override
public void remove(K key) {
root = remove(key, root);
size.getAndDecrement();
}

@Override
public void clear() {
root = clear(root);
size.set(0);
}

@Override
public Integer size() {
return size.get();
}

private Collection<Node<K, V>> getNodeValue(Node<K, V> node) {
if (node == null) {
return new ArrayList<>();
}

if (node.getLeft() == null && node.getRight() == null) {
return new ArrayList<>(List.of(node));
}

if (node.getLeft() == null) {
var arr = getNodeValue(node.getRight());
arr.add(node);
return arr;
}

if (node.getRight() == null) {
var arr = getNodeValue(node.getLeft());
arr.add(node);
return arr;
}

var result = new ArrayList<>(getNodeValue(node.getLeft()));
result.addAll(getNodeValue(node.getRight()));
result.add(node);
return result;
}

private Optional<Node<K, V>> find(K key, Node<K, V> target) {
if (target == null) {
return Optional.empty();
}

// search finished
if (key.compareTo(target.getKey()) == 0) {
return Optional.of(target);
}

// key is most probably on the right side of the tree
if (key.compareTo(target.getKey()) > 0) {
return find(key, target.getRight());
}

// key is most probably on the left side of the tree
return find(key, target.getLeft());
}

private Node<K, V> remove(K key, Node<K, V> target) {
if (target == null) {
return null;
}

// key < 0, then remove left child
if (key.compareTo(target.getKey()) < 0) {
target.setLeft(remove(key, target.getLeft()));
return target;
}

// key < 0, then remove right child
if (key.compareTo(target.getKey()) > 0) {
target.setRight(remove(key, target.getRight()));
return target;
}

// if no children, then just remove the node
if (target.getLeft() == null && target.getRight() == null) {
return null;
}

// if left child is present, then replace parent with child
if (target.getLeft() == null) {
target = target.getRight();
return target;
}

// if right child is present, then replace parent with child
if (target.getRight() == null) {
target = target.getLeft();
return target;
}

// in case of both children are present, replace with the minimum key
var min = min(target.getRight());
target.setKey(min);
target.setRight(remove(min, target.getRight()));
return target;
}

private K min(Node<K, V> node) {
while (node.getLeft() != null) {
node = node.getLeft();
}
return node.getKey();
}

private Node<K, V> clear(Node<K, V> target) {
if (target != null) {
clear(target.getRight());
clear(target.getLeft());
}
return null;
}
}
16 changes: 16 additions & 0 deletions stundb/api/src/main/java/com/stundb/api/btree/Node.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.stundb.api.btree;

import lombok.Data;

@Data
public class Node<K extends Comparable<K>, V> {
private K key;
private V value;
private Node<K, V> left;
private Node<K, V> right;

public Node(K key, V value) {
this.key = key;
this.value = value;
}
}
4 changes: 4 additions & 0 deletions stundb/api/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
requires transitive org.slf4j;
requires transitive jakarta.inject;

exports com.stundb.api.btree;
exports com.stundb.api.configuration;
exports com.stundb.api.crdt;
exports com.stundb.api.mappers;
exports com.stundb.api.models;
exports com.stundb.api.providers;

opens com.stundb.api.btree to
com.google.guice,
com.fasterxml.jackson.databind;
opens com.stundb.api.configuration to
com.google.guice,
com.fasterxml.jackson.databind,
Expand Down
24 changes: 16 additions & 8 deletions stundb/application/src/main/java/com/stundb/modules/Module.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.Matchers;
import com.google.inject.name.Names;
import com.stundb.api.btree.BTree;
import com.stundb.api.configuration.ConfigurationLoader;
import com.stundb.api.mappers.ApplicationConfigMapper;
import com.stundb.api.models.ApplicationConfig;
Expand All @@ -25,6 +26,7 @@
import com.stundb.service.*;
import com.stundb.service.impl.*;
import com.stundb.timers.CoordinatorTimerTask;

import jakarta.validation.Validator;

import java.security.MessageDigest;
Expand All @@ -41,19 +43,25 @@ protected void configure() {
bindInterceptor(
Matchers.any(), Matchers.annotatedWith(Loggable.class), new RequestLogger());

bind(new TypeLiteral<Cache<Object>>() {}).toProvider(PublicCacheProvider.class);
bind(new TypeLiteral<Cache<Node>>() {}).toProvider(InternalCacheProvider.class);
bind(new TypeLiteral<BTree<String, Object>>() {})
.toProvider(new TypeLiteral<BTreeProvider<Object>>() {})
.in(Singleton.class);
bind(new TypeLiteral<BTree<String, Node>>() {})
.toProvider(new TypeLiteral<BTreeProvider<Node>>() {})
.in(Singleton.class);
bind(new TypeLiteral<Cache<Object>>() {})
.toProvider(new TypeLiteral<CacheProvider<Object>>() {})
.in(Singleton.class);
bind(new TypeLiteral<Cache<Node>>() {})
.toProvider(new TypeLiteral<CacheProvider<Node>>() {})
.in(Singleton.class);

bind(ConfigurationLoader.class).toInstance(new ConfigurationLoader());
bind(ApplicationConfig.class)
.toProvider(ApplicationConfigProvider.class)
.in(Singleton.class);
bind(Validator.class)
.toProvider(ValidatorProvider.class)
.in(Singleton.class);
bind(ObjectMapper.class)
.toProvider(ObjectMapperProvider.class)
.in(Singleton.class);
bind(Validator.class).toProvider(ValidatorProvider.class).in(Singleton.class);
bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class).in(Singleton.class);
bind(MessageDigest.class).toProvider(MessageDigestProvider.class);
bind(UniqueId.class).toProvider(UniqueIdProvider.class);
bind(ApplicationConfigMapper.class).toInstance(ApplicationConfigMapper.INSTANCE);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.stundb.modules.providers;

import com.stundb.api.btree.BTree;
import com.stundb.api.btree.BTreeImpl;

import jakarta.inject.Provider;

import java.util.Optional;

public class BTreeProvider<T> implements Provider<BTree<String, T>> {

private BTreeImpl<String, T> instance;

@Override
public BTree<String, T> get() {
instance = Optional.ofNullable(instance).orElseGet(BTreeImpl::new);
return instance;
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
package com.stundb.modules.providers;

import static java.util.Optional.ofNullable;

import com.stundb.api.models.ApplicationConfig;
import com.stundb.api.models.Capacity;
import com.stundb.core.cache.BTreeCache;
import com.stundb.core.cache.Cache;
import com.stundb.core.cache.FIFOCache;

import java.util.function.Function;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.inject.Singleton;

@Singleton
public class CacheProvider<T> implements Provider<Cache<T>> {

@Inject private BTreeCache<T> cache;

interface CacheProvider<T> {
default Cache<T> getInstance(
Cache<T> cache,
ApplicationConfig config,
Function<Capacity, Integer> transformer,
Integer defaultCapacity) {
return ofNullable(cache)
.orElseGet(
() ->
new FIFOCache<>(
ofNullable(config.getCapacity())
.map(transformer)
.orElse(defaultCapacity)));
@Override
public Cache<T> get() {
return cache;
}
}

This file was deleted.

This file was deleted.

Loading

0 comments on commit cb0358e

Please sign in to comment.