4 changed files with 195 additions and 0 deletions
@ -0,0 +1,22 @@ |
|||||
|
package org.thingsboard.server.dao.edq; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
|
||||
|
public class DeviceRepoData { |
||||
|
@Getter |
||||
|
private final Device device; |
||||
|
@Getter |
||||
|
private final Map<Integer, String> attrs = new ConcurrentHashMap<>(); |
||||
|
|
||||
|
public DeviceRepoData(Device device) { |
||||
|
this.device = device; |
||||
|
} |
||||
|
|
||||
|
public void putAttr(Integer keyId, String value){ |
||||
|
attrs.put(keyId, value); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,103 @@ |
|||||
|
package org.thingsboard.server.dao.edq; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.StringUtils; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Comparator; |
||||
|
import java.util.List; |
||||
|
import java.util.UUID; |
||||
|
import java.util.function.Function; |
||||
|
import java.util.function.Predicate; |
||||
|
|
||||
|
@Slf4j |
||||
|
public class EntityDataQueryTest { |
||||
|
|
||||
|
private static final int DEVICE_COUNT = 500000; |
||||
|
private static final int ATTR_COUNT = 100; |
||||
|
private static final Comparator<DeviceRepoData> CREATED_TIME_COMPARATOR = Comparator.comparingLong(o -> o.getDevice().getCreatedTime()); |
||||
|
|
||||
|
private static final Comparator<DeviceRepoData> ATTR_COMPARATOR = Comparator.comparing(o -> o.getAttrs().get(10)); |
||||
|
|
||||
|
private static final Comparator<SortPair> SORT_ASC = Comparator.comparing(SortPair::getKey); |
||||
|
private static final Comparator<SortPair> SORT_DESC = Comparator.comparing(SortPair::getKey).reversed(); |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
InMemoryRepository repository = new InMemoryRepository(); |
||||
|
long startTs = System.currentTimeMillis(); |
||||
|
long ts = System.currentTimeMillis() - DEVICE_COUNT; |
||||
|
for (int i = 0; i < DEVICE_COUNT; i++) { |
||||
|
DeviceId deviceId = new DeviceId(UUID.randomUUID()); |
||||
|
Device device = new Device(); |
||||
|
device.setId(deviceId); |
||||
|
device.setCreatedTime(ts + i); |
||||
|
device.setName("Device " + i); |
||||
|
device.setLabel("Device Label" + i); |
||||
|
device.setType("Device Type " + (i % 100)); |
||||
|
repository.add(device); |
||||
|
String random = StringUtils.randomAlphanumeric(5); |
||||
|
for (int j = 0; j < ATTR_COUNT; j++) { |
||||
|
repository.add(deviceId, j, random); |
||||
|
} |
||||
|
} |
||||
|
log.error("Repository created in {}", System.currentTimeMillis() - startTs); |
||||
|
|
||||
|
|
||||
|
for (int i = 0; i < 1; i++) { |
||||
|
test("DeviceName filter, sort by attribute desc", repository, 10, d -> d.getDevice().getName().startsWith("Device 9"), ATTR_COMPARATOR.reversed()); |
||||
|
test2("DeviceName filter, sort by attribute desc", repository, 10, d -> d.getDevice().getName().startsWith("Device 8"), d -> d.getAttrs().get(10), SORT_DESC); |
||||
|
|
||||
|
test("DeviceType filter, no sort", repository, 3, d -> d.getDevice().getType().equals("Device Type 10"), null); |
||||
|
test2("DeviceType filter, no sort", repository, 3, d -> d.getDevice().getType().equals("Device Type 10"), null, null); |
||||
|
|
||||
|
test("Attribute filter, no sort", repository, 3, d -> d.getAttrs().get(10).contains("1"), null); |
||||
|
test2("Attribute filter, no sort", repository, 3, d -> d.getAttrs().get(9).contains("2"), null, null); |
||||
|
|
||||
|
test("No filter, sort by attribute", repository, 3, null, ATTR_COMPARATOR); |
||||
|
test2("No filter, sort by attribute", repository, 3, null, d -> d.getAttrs().get(10), SORT_ASC); |
||||
|
|
||||
|
test("Attribute filter, createdTime", repository, 3, d -> d.getAttrs().get(10).contains("1"), CREATED_TIME_COMPARATOR); |
||||
|
test("No filter, sort by createdTime", repository, 3, null, CREATED_TIME_COMPARATOR); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void test(String testName, InMemoryRepository repository, int pages, Predicate<DeviceRepoData> predicate, Comparator<DeviceRepoData> comparator) { |
||||
|
log.error("==================================================================================================="); |
||||
|
log.error("============================ 1 = {} =======================================", testName); |
||||
|
List<Long> execTimes = new ArrayList<>(); |
||||
|
for (int i = 0; i < pages; i++) { |
||||
|
long startTs = System.nanoTime(); |
||||
|
var results = repository.findDevices(i, 10, predicate, comparator); |
||||
|
if (results.isEmpty()) { |
||||
|
log.error("Empty results"); |
||||
|
} |
||||
|
long endTs = System.nanoTime(); |
||||
|
execTimes.add(endTs - startTs); |
||||
|
} |
||||
|
log.error("Run queries in {}", execTimes); |
||||
|
log.error("Average {}", execTimes.stream().mapToDouble(v -> (double) v).average().orElse(0.0) / 1000000.0); |
||||
|
log.error("==================================================================================================="); |
||||
|
} |
||||
|
|
||||
|
private static void test2(String testName, InMemoryRepository repository, int pages, Predicate<DeviceRepoData> predicate, Function<DeviceRepoData, String> toSortKey, Comparator<SortPair> comparator) { |
||||
|
log.error("==================================================================================================="); |
||||
|
log.error("=========================== 2 = {} =======================================", testName); |
||||
|
List<Long> execTimes = new ArrayList<>(); |
||||
|
for (int i = 0; i < pages; i++) { |
||||
|
long startTs = System.nanoTime(); |
||||
|
var results = repository.findDevices(i, 10, predicate, toSortKey, comparator); |
||||
|
if (results.isEmpty()) { |
||||
|
log.error("Empty results"); |
||||
|
} |
||||
|
long endTs = System.nanoTime(); |
||||
|
execTimes.add(endTs - startTs); |
||||
|
} |
||||
|
log.error("Run queries in {}", execTimes); |
||||
|
log.error("Average {}", execTimes.stream().mapToDouble(v -> (double) v).average().orElse(0.0) / 1000000.0); |
||||
|
log.error("==================================================================================================="); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
package org.thingsboard.server.dao.edq; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Comparator; |
||||
|
import java.util.List; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
import java.util.concurrent.ConcurrentMap; |
||||
|
import java.util.concurrent.ConcurrentSkipListSet; |
||||
|
import java.util.function.Function; |
||||
|
import java.util.function.Predicate; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
public class InMemoryRepository { |
||||
|
|
||||
|
private final static ConcurrentSkipListSet<DeviceRepoData> devices = new ConcurrentSkipListSet<>(Comparator.comparingLong(o -> o.getDevice().getCreatedTime())); |
||||
|
private final static ConcurrentMap<DeviceId, DeviceRepoData> devicesById = new ConcurrentHashMap<>(); |
||||
|
|
||||
|
public void add(Device device) { |
||||
|
var dd = new DeviceRepoData(device); |
||||
|
devicesById.put(dd.getDevice().getId(), dd); |
||||
|
devices.add(dd); |
||||
|
} |
||||
|
|
||||
|
public void add(DeviceId deviceId, Integer attrKey, String attrValue) { |
||||
|
devicesById.get(deviceId).putAttr(attrKey, attrValue); |
||||
|
} |
||||
|
|
||||
|
public List<DeviceRepoData> findDevices(int page, int pageSize, Predicate<DeviceRepoData> predicate, Comparator<DeviceRepoData> comparator) { |
||||
|
var stream = devices.stream(); |
||||
|
if (predicate != null) { |
||||
|
stream = stream.filter(predicate); |
||||
|
} |
||||
|
if (comparator != null) { |
||||
|
stream = stream.sorted(comparator); |
||||
|
} |
||||
|
return stream.skip(page * pageSize).limit(pageSize).collect(Collectors.toList()); |
||||
|
} |
||||
|
|
||||
|
public List<DeviceRepoData> findDevices(int page, int pageSize, Predicate<DeviceRepoData> predicate, Function<DeviceRepoData, String> sortFieldFunction, Comparator<SortPair> comparator) { |
||||
|
var stream = devices.stream(); |
||||
|
if (predicate != null) { |
||||
|
stream = stream.filter(predicate); |
||||
|
} |
||||
|
|
||||
|
if (comparator != null) { |
||||
|
var sortedStream = stream.map(d -> new SortPair(sortFieldFunction.apply(d), d)); |
||||
|
sortedStream = sortedStream.sorted(comparator); |
||||
|
return sortedStream.skip(page * pageSize).limit(pageSize).map(v -> v.data).collect(Collectors.toList()); |
||||
|
} else { |
||||
|
return stream.skip(page * pageSize).limit(pageSize).collect(Collectors.toList()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
package org.thingsboard.server.dao.edq; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
|
||||
|
@RequiredArgsConstructor |
||||
|
public class SortPair { |
||||
|
|
||||
|
@Getter |
||||
|
final String key; |
||||
|
@Getter |
||||
|
final DeviceRepoData data; |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue