From 603ceedff999d7ea0d03215a61cfb61f58b098fd Mon Sep 17 00:00:00 2001 From: Wouter Born Date: Sun, 1 Nov 2020 21:28:25 +0100 Subject: [PATCH] [rrd4j] Improve performance (#8929) * [rrd4j] Improve performance * Cache item to improve performance * Also return DecimalType for QuantityType values Related to #8928 Reintroduces #8809 Signed-off-by: Wouter Born --- .../internal/RRD4jPersistenceService.java | 152 +++++++++--------- 1 file changed, 75 insertions(+), 77 deletions(-) diff --git a/bundles/org.openhab.persistence.rrd4j/src/main/java/org/openhab/persistence/rrd4j/internal/RRD4jPersistenceService.java b/bundles/org.openhab.persistence.rrd4j/src/main/java/org/openhab/persistence/rrd4j/internal/RRD4jPersistenceService.java index 79fac1505..f846da2cd 100644 --- a/bundles/org.openhab.persistence.rrd4j/src/main/java/org/openhab/persistence/rrd4j/internal/RRD4jPersistenceService.java +++ b/bundles/org.openhab.persistence.rrd4j/src/main/java/org/openhab/persistence/rrd4j/internal/RRD4jPersistenceService.java @@ -18,7 +18,6 @@ import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -154,7 +153,7 @@ public class RRD4jPersistenceService implements QueryablePersistenceService { sample.setValue(DATASOURCE_STATE, lastValue); sample.update(); logger.debug("Stored '{}' with state '{}' in rrd4j database (again)", name, - mapToState(lastValue, item.getName())); + mapToState(lastValue, item)); } } } catch (IOException e) { @@ -199,7 +198,8 @@ public class RRD4jPersistenceService implements QueryablePersistenceService { logger.debug("Stored '{}' with state '{}' in rrd4j database", name, value); } } catch (IllegalArgumentException e) { - if (e.getMessage().contains("at least one second step is required")) { + String message = e.getMessage(); + if (message != null && message.contains("at least one second step is required")) { // we try to store the value one second later ScheduledFuture job = scheduledJobs.get(name); if (job != null) { @@ -230,73 +230,83 @@ public class RRD4jPersistenceService implements QueryablePersistenceService { @Override public Iterable query(FilterCriteria filter) { String itemName = filter.getItemName(); - RrdDb db = getDB(itemName); - if (db != null) { - ConsolFun consolidationFunction = getConsolidationFunction(db); - long start = 0L; - long end = filter.getEndDate() == null ? System.currentTimeMillis() / 1000 - : filter.getEndDate().toInstant().getEpochSecond(); - try { - if (filter.getBeginDate() == null) { - // as rrd goes back for years and gets more and more - // inaccurate, we only support descending order - // and a single return value - // if there is no begin date is given - this case is - // required specifically for the historicState() - // query, which we want to support - if (filter.getOrdering() == Ordering.DESCENDING && filter.getPageSize() == 1 - && filter.getPageNumber() == 0) { - if (filter.getEndDate() == null) { - // we are asked only for the most recent value! - double lastValue = db.getLastDatasourceValue(DATASOURCE_STATE); - if (!Double.isNaN(lastValue)) { - HistoricItem rrd4jItem = new RRD4jItem(itemName, mapToState(lastValue, itemName), - ZonedDateTime.ofInstant( - Instant.ofEpochMilli(db.getLastArchiveUpdateTime() * 1000), - ZoneId.systemDefault())); - return Collections.singletonList(rrd4jItem); - } else { - return Collections.emptyList(); - } + RrdDb db = getDB(itemName); + if (db == null) { + logger.debug("Could not find item '{}' in rrd4j database", itemName); + return List.of(); + } + + Item item = null; + try { + item = itemRegistry.getItem(itemName); + } catch (ItemNotFoundException e) { + logger.debug("Could not find item '{}' in registry", itemName); + } + + long start = 0L; + long end = filter.getEndDate() == null ? System.currentTimeMillis() / 1000 + : filter.getEndDate().toInstant().getEpochSecond(); + + try { + if (filter.getBeginDate() == null) { + // as rrd goes back for years and gets more and more + // inaccurate, we only support descending order + // and a single return value + // if there is no begin date is given - this case is + // required specifically for the historicState() + // query, which we want to support + if (filter.getOrdering() == Ordering.DESCENDING && filter.getPageSize() == 1 + && filter.getPageNumber() == 0) { + if (filter.getEndDate() == null) { + // we are asked only for the most recent value! + double lastValue = db.getLastDatasourceValue(DATASOURCE_STATE); + if (!Double.isNaN(lastValue)) { + HistoricItem rrd4jItem = new RRD4jItem(itemName, mapToState(lastValue, item), + ZonedDateTime.ofInstant(Instant.ofEpochMilli(db.getLastArchiveUpdateTime() * 1000), + ZoneId.systemDefault())); + return List.of(rrd4jItem); } else { - start = end; + return List.of(); } } else { - throw new UnsupportedOperationException("rrd4j does not allow querys without a begin date, " - + "unless order is descending and a single value is requested"); + start = end; } } else { - start = filter.getBeginDate().toInstant().getEpochSecond(); + throw new UnsupportedOperationException("rrd4j does not allow querys without a begin date, " + + "unless order is descending and a single value is requested"); } - FetchRequest request = db.createFetchRequest(consolidationFunction, start, end, 1); - - List items = new ArrayList<>(); - FetchData result = request.fetchData(); - long ts = result.getFirstTimestamp(); - long step = result.getRowCount() > 1 ? result.getStep() : 0; - for (double value : result.getValues(DATASOURCE_STATE)) { - if (!Double.isNaN(value) && (((ts >= start) && (ts <= end)) || (start == end))) { - RRD4jItem rrd4jItem = new RRD4jItem(itemName, mapToState(value, itemName), - ZonedDateTime.ofInstant(Instant.ofEpochMilli(ts * 1000), ZoneId.systemDefault())); - items.add(rrd4jItem); - } - ts += step; - } - return items; - } catch (IOException e) { - logger.warn("Could not query rrd4j database for item '{}': {}", itemName, e.getMessage()); + } else { + start = filter.getBeginDate().toInstant().getEpochSecond(); } + + FetchRequest request = db.createFetchRequest(getConsolidationFunction(db), start, end, 1); + FetchData result = request.fetchData(); + + List items = new ArrayList<>(); + long ts = result.getFirstTimestamp(); + long step = result.getRowCount() > 1 ? result.getStep() : 0; + for (double value : result.getValues(DATASOURCE_STATE)) { + if (!Double.isNaN(value) && (((ts >= start) && (ts <= end)) || (start == end))) { + RRD4jItem rrd4jItem = new RRD4jItem(itemName, mapToState(value, item), + ZonedDateTime.ofInstant(Instant.ofEpochMilli(ts * 1000), ZoneId.systemDefault())); + items.add(rrd4jItem); + } + ts += step; + } + return items; + } catch (IOException e) { + logger.warn("Could not query rrd4j database for item '{}': {}", itemName, e.getMessage()); + return List.of(); } - return Collections.emptyList(); } @Override public Set getItemInfo() { - return Collections.emptySet(); + return Set.of(); } - protected @Nullable synchronized RrdDb getDB(String alias) { + protected synchronized @Nullable RrdDb getDB(String alias) { RrdDb db = null; File file = new File(DB_FOLDER + File.separator + alias + ".rrd"); try { @@ -383,29 +393,17 @@ public class RRD4jPersistenceService implements QueryablePersistenceService { } } - @SuppressWarnings({ "rawtypes", "unchecked" }) - private State mapToState(double value, String itemName) { - try { - Item item = itemRegistry.getItem(itemName); - if (item instanceof SwitchItem && !(item instanceof DimmerItem)) { - return value == 0.0d ? OnOffType.OFF : OnOffType.ON; - } else if (item instanceof ContactItem) { - return value == 0.0d ? OpenClosedType.CLOSED : OpenClosedType.OPEN; - } else if (item instanceof DimmerItem || item instanceof RollershutterItem) { - // make sure Items that need PercentTypes instead of DecimalTypes do receive the right information - return new PercentType((int) Math.round(value * 100)); - } else if (item instanceof NumberItem) { - Unit> unit = ((NumberItem) item).getUnit(); - if (unit != null) { - return new QuantityType(value, unit); - } else { - return new DecimalType(value); - } - } - } catch (ItemNotFoundException e) { - logger.debug("Could not find item '{}' in registry", itemName); + private State mapToState(double value, @Nullable Item item) { + if (item instanceof SwitchItem && !(item instanceof DimmerItem)) { + return value == 0.0d ? OnOffType.OFF : OnOffType.ON; + } else if (item instanceof ContactItem) { + return value == 0.0d ? OpenClosedType.CLOSED : OpenClosedType.OPEN; + } else if (item instanceof DimmerItem || item instanceof RollershutterItem) { + // make sure Items that need PercentTypes instead of DecimalTypes do receive the right information + return new PercentType((int) Math.round(value * 100)); } - // just return a DecimalType as a fallback + // return a DecimalType as a fallback and for QuantityType values to prevent performance issues + // see: https://github.com/openhab/openhab-addons/issues/8928 return new DecimalType(value); }