package com.cube.index;
import com.cube.storage.StorageEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
/**
* Cubic Indexed Storage Engine - Combines LSM storage with cubic indexing.
*
* Features:
* - Cubic index tree (N³×6) for fast lookups
* - 6-sided distribution for load balancing
* - Auto-expanding levels
* - Prefix and range queries
*/
public class CubicIndexedStorage implements StorageEngine {
private static final Logger logger = LoggerFactory.getLogger(CubicIndexedStorage.class);
private final StorageEngine backingStorage;
private final CubicIndexTree index;
private final boolean indexEnabled;
public CubicIndexedStorage(StorageEngine backingStorage) {
this(backingStorage, true, 5, 20);
}
public CubicIndexedStorage(
StorageEngine backingStorage,
boolean indexEnabled,
int initialLevels,
int maxLevels) {
this.backingStorage = backingStorage;
this.indexEnabled = indexEnabled;
this.index = indexEnabled ? new CubicIndexTree(initialLevels, maxLevels, true) : null;
logger.info("Cubic indexed storage initialized (indexing: {})", indexEnabled);
}
@Override
public void put(String key, byte[] value) throws IOException {
// Write to backing storage
backingStorage.put(key, value);
// Update index
if (indexEnabled) {
index.put(key, value);
}
}
@Override
public byte[] get(String key) throws IOException {
// Try index first for fast lookup
if (indexEnabled) {
byte[] value = index.get(key);
if (value != null) {
return value;
}
}
// Fallback to backing storage
return backingStorage.get(key);
}
@Override
public boolean delete(String key) throws IOException {
// Remove from index
if (indexEnabled) {
index.remove(key);
}
// Delete from backing storage
return backingStorage.delete(key);
}
@Override
public Iterator<String> scan(String prefix) throws IOException {
if (indexEnabled) {
// Use index for fast prefix search
List<String> results = index.searchPrefix(prefix);
return results.iterator();
}
// Fallback to backing storage
return backingStorage.scan(prefix);
}
@Override
public Iterator<Map.Entry<String, byte[]>> scanEntries(String prefix) throws IOException {
if (indexEnabled) {
// Use index for fast prefix search
List<String> keys = index.searchPrefix(prefix);
List<Map.Entry<String, byte[]>> entries = new ArrayList<>();
for (String key : keys) {
byte[] value = index.get(key);
if (value != null) {
entries.add(new AbstractMap.SimpleEntry<>(key, value));
}
}
return entries.iterator();
}
// Fallback to backing storage
return backingStorage.scanEntries(prefix);
}
/**
* Range search using cubic index
*/
public List<String> rangeSearch(String startKey, String endKey) {
if (!indexEnabled) {
throw new UnsupportedOperationException("Index is disabled");
}
return index.searchRange(startKey, endKey);
}
/**
* Get keys at a specific cubic level
*/
public Set<String> getKeysAtLevel(int level) {
if (!indexEnabled) {
throw new UnsupportedOperationException("Index is disabled");
}
CubicIndexNode node = index.getLevel(level);
return node != null ? node.getAllKeys() : Collections.emptySet();
}
/**
* Get keys on a specific side of a level
*/
public Set<String> getKeysOnSide(int level, CubicIndexNode.Side side) {
if (!indexEnabled) {
throw new UnsupportedOperationException("Index is disabled");
}
CubicIndexNode node = index.getLevel(level);
return node != null ? node.getSide(side).keys() : Collections.emptySet();
}
/**
* Rebalance the cubic index
*/
public void rebalanceIndex() {
if (!indexEnabled) {
return;
}
index.rebalance();
logger.info("Cubic index rebalanced");
}
/**
* Rebuild index from backing storage
*/
public void rebuildIndex() throws IOException {
if (!indexEnabled) {
return;
}
logger.info("Rebuilding cubic index from storage...");
index.clear();
// Scan all keys from backing storage
Iterator<Map.Entry<String, byte[]>> entries = backingStorage.scanEntries("");
int count = 0;
while (entries.hasNext()) {
Map.Entry<String, byte[]> entry = entries.next();
index.put(entry.getKey(), entry.getValue());
count++;
}
logger.info("Rebuilt cubic index with {} keys", count);
}
@Override
public void flush() throws IOException {
backingStorage.flush();
}
@Override
public void compact() throws IOException {
backingStorage.compact();
}
@Override
public StorageStats getStats() {
StorageStats backingStats = backingStorage.getStats();
if (!indexEnabled) {
return backingStats;
}
// Combine backing storage stats with index stats
Map<String, Object> indexStats = index.getStats();
return new StorageStats(
backingStats.getTotalKeys(),
backingStats.getTotalSize(),
backingStats.getMemtableSize(),
backingStats.getSstableCount()
);
}
/**
* Get cubic index statistics
*/
public Map<String, Object> getIndexStats() {
if (!indexEnabled) {
return Collections.emptyMap();
}
return index.getStats();
}
/**
* Print cubic index structure
*/
public void printIndexStructure() {
if (indexEnabled) {
index.printStructure();
}
}
/**
* Get the cubic index tree (for advanced operations)
*/
public CubicIndexTree getIndex() {
return index;
}
public boolean isIndexEnabled() {
return indexEnabled;
}
@Override
public void close() throws IOException {
if (indexEnabled) {
index.clear();
}
backingStorage.close();
}
}