Skip to content

Commit

Permalink
feature(db): optimize properties query (#5378)
Browse files Browse the repository at this point in the history
  • Loading branch information
CarlChaoCarl authored Sep 4, 2023
1 parent ad728fa commit 068764b
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public abstract class AbstractSnapshot<K, V> implements Snapshot {

protected WeakReference<Snapshot> next;

protected boolean isOptimized;

@Override
public Snapshot advance() {
return new SnapshotImpl(this);
Expand All @@ -34,4 +36,9 @@ public void setNext(Snapshot next) {
public String getDbName() {
return db.getDbName();
}

@Override
public boolean isOptimized(){
return isOptimized;
}
}
4 changes: 4 additions & 0 deletions chainbase/src/main/java/org/tron/core/db2/core/Snapshot.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,8 @@ static boolean isImpl(Snapshot snapshot) {
void updateSolidity();

String getDbName();

boolean isOptimized();

void reloadToMem();
}
23 changes: 23 additions & 0 deletions chainbase/src/main/java/org/tron/core/db2/core/SnapshotImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public class SnapshotImpl extends AbstractSnapshot<Key, Value> {
}
previous = snapshot;
snapshot.setNext(this);
isOptimized = snapshot.isOptimized();
if (isOptimized && root == previous) {
Streams.stream(root.iterator()).forEach( e -> put(e.getKey(),e.getValue()));
}
}

@Override
Expand All @@ -40,6 +44,7 @@ public byte[] get(byte[] key) {
private byte[] get(Snapshot head, byte[] key) {
Snapshot snapshot = head;
Value value;

while (Snapshot.isImpl(snapshot)) {
if ((value = ((SnapshotImpl) snapshot).db.get(Key.of(key))) != null) {
return value.getBytes();
Expand Down Expand Up @@ -83,6 +88,19 @@ public void merge(Snapshot from) {
Streams.stream(fromImpl.db).forEach(e -> db.put(e.getKey(), e.getValue()));
}

public void mergeAhead(Snapshot from) {
if (from instanceof SnapshotRoot) {
return ;
}
SnapshotImpl fromImpl = (SnapshotImpl) from;
Streams.stream(fromImpl.db).forEach(e -> {
if (db.get(e.getKey()) == null) {
db.put(e.getKey(), e.getValue());
}
}
);
}

@Override
public Snapshot retreat() {
return previous;
Expand Down Expand Up @@ -177,4 +195,9 @@ public String getDbName() {
public Snapshot newInstance() {
return new SnapshotImpl(this);
}

@Override
public void reloadToMem() {
mergeAhead(previous);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,12 @@ public synchronized void commit() {
}

--activeSession;

dbs.forEach(db -> {
if (db.getHead().isOptimized()) {
db.getHead().reloadToMem();
}
});
}

public synchronized void pop() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public SnapshotRoot(DB<byte[], byte[]> db) {
if (CACHE_DBS.contains(this.db.getDbName())) {
this.cache = CacheManager.allocate(CacheType.findByType(this.db.getDbName()));
}
isOptimized = "properties".equalsIgnoreCase(db.getDbName());
}

private boolean needOptAsset() {
Expand Down Expand Up @@ -221,4 +222,7 @@ public String getDbName() {
public Snapshot newInstance() {
return new SnapshotRoot(db.newInstance());
}

@Override
public void reloadToMem() { }
}
201 changes: 201 additions & 0 deletions framework/src/test/java/org/tron/core/db2/SnapshotImplTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package org.tron.core.db2;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import java.io.File;
import java.lang.reflect.Constructor;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.tron.common.application.Application;
import org.tron.common.application.ApplicationFactory;
import org.tron.common.application.TronApplicationContext;
import org.tron.common.utils.FileUtil;
import org.tron.core.Constant;
import org.tron.core.config.DefaultConfig;
import org.tron.core.config.args.Args;
import org.tron.core.db2.core.Snapshot;
import org.tron.core.db2.core.SnapshotImpl;
import org.tron.core.db2.core.SnapshotManager;
import org.tron.core.db2.core.SnapshotRoot;

public class SnapshotImplTest {
private RevokingDbWithCacheNewValueTest.TestRevokingTronStore tronDatabase;
private TronApplicationContext context;
private Application appT;
private SnapshotManager revokingDatabase;

@Before
public void init() {
Args.setParam(new String[]{"-d", "output_revokingStore_test"}, Constant.TEST_CONF);
context = new TronApplicationContext(DefaultConfig.class);
appT = ApplicationFactory.create(context);

tronDatabase = new RevokingDbWithCacheNewValueTest.TestRevokingTronStore(
"testSnapshotRoot-testMerge");
revokingDatabase = context.getBean(SnapshotManager.class);
revokingDatabase.enable();
revokingDatabase.add(tronDatabase.getRevokingDB());
}

@After
public void removeDb() {
Args.clearParam();
context.destroy();
FileUtil.deleteDir(new File("output_revokingStore_test"));

tronDatabase.close();
revokingDatabase.shutdown();
}

/**
* linklist is: from -> root
* root:key1=>value1, key2=>value2
* from:key3=>value3, key4=>value4
* after construct, getSnapshotImplIns(root);
* from: key1=>value1, key2=>value2, key3=>value3, key4=>value4
* from: get key1 or key2, traverse 0 times
*/
@Test
public void testMergeRoot() {
// linklist is: from -> root
SnapshotRoot root = new SnapshotRoot(tronDatabase.getDb());
//root.setOptimized(true);

root.put("key1".getBytes(), "value1".getBytes());
root.put("key2".getBytes(), "value2".getBytes());
SnapshotImpl from = getSnapshotImplIns(root);
from.put("key3".getBytes(), "value3".getBytes());
from.put("key4".getBytes(), "value4".getBytes());

byte[] s1 = from.get("key1".getBytes());
assertEquals(new String("value1".getBytes()), new String(s1));
byte[] s2 = from.get("key2".getBytes());
assertEquals(new String("value2".getBytes()), new String(s2));
}

/**
* linklist is: from2 -> from -> root
* root:
* from:key1=>value1, key2=>value2
* from2:key3=>value3,key4=>value4
* before merge: from2.mergeAhead(from);
* from2: get key1 or key2, traverse 1 times
* after merge
* from2:key1=>value1, key2=>value2, value3=>value3,key4=>value4
* from2: get key1 or key2, traverse 0 times
*
*/
@Test
public void testMergeAhead() {

// linklist is: from2 -> from -> root
SnapshotRoot root = new SnapshotRoot(tronDatabase.getDb());
SnapshotImpl from = getSnapshotImplIns(root);
from.put("key1".getBytes(), "value1".getBytes());
from.put("key2".getBytes(), "value2".getBytes());

SnapshotImpl from2 = getSnapshotImplIns(from);
from2.put("key3".getBytes(), "value3".getBytes());
from2.put("key4".getBytes(), "value4".getBytes());

/*
// before merge get data in from is success,traverse 0 times
byte[] s1 = from.get("key1".getBytes());
assertEquals(new String("value1".getBytes()), new String(s1));
byte[] s2 = from.get("key2".getBytes());
assertEquals(new String("value2".getBytes()), new String(s2));
// before merge get data in from2 is success, traverse 0 times
byte[] s3 = from2.get("key3".getBytes());
assertEquals(new String("value3".getBytes()), new String(s3));
byte[] s4 = from2.get("key4".getBytes());
assertEquals(new String("value4".getBytes()), new String(s4));
*/

// before merge from2 get data is success, traverse 1 times
byte[] s11 = from2.get("key1".getBytes());
assertEquals(new String("value1".getBytes()), new String(s11));
byte[] s12 = from2.get("key2".getBytes());
assertEquals(new String("value2".getBytes()), new String(s12));
// this can not get key3 and key4
assertNull(from.get("key3".getBytes()));
assertNull(from.get("key4".getBytes()));

// do mergeAhead
from2.mergeAhead(from);
/*
// after merge get data in from is success, traverse 0 times
s1 = from.get("key1".getBytes());
assertEquals(new String("value1".getBytes()), new String(s1));
s2 = from.get("key2".getBytes());
assertEquals(new String("value2".getBytes()), new String(s2));
// after merge get data in from2 is success, traverse 0 times
s3 = from2.get("key3".getBytes());
assertEquals(new String("value3".getBytes()), new String(s3));
s4 = from2.get("key4".getBytes());
assertEquals(new String("value4".getBytes()), new String(s4));
*/

// after merge from2 get data is success, traverse 0 times
byte[] s1 = from2.get("key1".getBytes());
assertEquals(new String("value1".getBytes()), new String(s1));
byte[] s2 = from2.get("key2".getBytes());
assertEquals(new String("value2".getBytes()), new String(s2));

// this can not get key3 and key4
assertNull(from.get("key3".getBytes()));
assertNull(from.get("key4".getBytes()));
}

/**
* from: key1=>value1, key2=>value2, key3=>value31
* from2: key3=>value32,key4=>value4
* after merge: from2.mergeAhead(from);
* from2: key1=>value1, key2=>value2, key3=>value32, key4=>value4
*/
@Test
public void testMergeOverride() {
// linklist is: from2 -> from -> root
SnapshotRoot root = new SnapshotRoot(tronDatabase.getDb());
SnapshotImpl from = getSnapshotImplIns(root);
from.put("key1".getBytes(), "value1".getBytes());
from.put("key2".getBytes(), "value2".getBytes());
from.put("key3".getBytes(), "value31".getBytes());

SnapshotImpl from2 = getSnapshotImplIns(from);
from2.put("key3".getBytes(), "value32".getBytes());
from2.put("key4".getBytes(), "value4".getBytes());
// do mergeAhead
from2.mergeAhead(from);

// after merge from2 get data is success, traverse 0 times
byte[] s1 = from2.get("key1".getBytes());
assertEquals(new String("value1".getBytes()), new String(s1));
byte[] s2 = from2.get("key2".getBytes());
assertEquals(new String("value2".getBytes()), new String(s2));
byte[] s3 = from2.get("key3".getBytes());
assertEquals(new String("value32".getBytes()), new String(s3));
byte[] s4 = from2.get("key4".getBytes());
assertEquals(new String("value4".getBytes()), new String(s4));
}

/**
* The constructor of SnapshotImpl is not public
* so reflection is used to construct the object here.
*/
private SnapshotImpl getSnapshotImplIns(Snapshot snapshot) {
Class clazz = SnapshotImpl.class;
try {
Constructor constructor = clazz.getDeclaredConstructor(Snapshot.class);
constructor.setAccessible(true);
return (SnapshotImpl) constructor.newInstance(snapshot);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

}

0 comments on commit 068764b

Please sign in to comment.