Commit 84cb471e0d7e2e49807a232b87c949d44e377fb2

Authored by ShvaykaD
Committed by Andrew Shvayka
1 parent cc0e5417

Sql timeseries improvements (#2033)

* init commit

* cleaned code and add test-properties

* cleaned code

* psql-update

* timescale-update

* code-refactoring

* fix typo

* renamed dao

* revert indents

* refactored code

* fix typo

* init-partitioning

* code updated

* cleaned code

* fixed license

* fix typo

* fixed code after review

* add annotation to repository

* update psql version for docker

* postgres-10

* postgres-10

* update docker compose config

* fixed partition saving

* change key_id to serial column definition

* upgrade psql added

* add separate upgrade service

* added upgrade script

* change image on k8s

* change logs

* resolve conflict after merge with master

* revert datasource url in yml

* fix typo

* license header fix

* remove old methods for the timeseries inserts

* clean up code

* fix saveOrUpdate for PostgreSQL

* refactoring & revert Timescale to use latest table

* added PsqlTsAnyDao

* duplicated code method removed

* remove unused invert dictionary map

* change the upgrade directory from 2.4.1 to 2.4.3

* refactor JpaPsqlTimeseriesDao
Showing 72 changed files with 2497 additions and 1245 deletions
  1 +--
  2 +-- Copyright © 2016-2020 The Thingsboard Authors
  3 +--
  4 +-- Licensed under the Apache License, Version 2.0 (the "License");
  5 +-- you may not use this file except in compliance with the License.
  6 +-- You may obtain a copy of the License at
  7 +--
  8 +-- http://www.apache.org/licenses/LICENSE-2.0
  9 +--
  10 +-- Unless required by applicable law or agreed to in writing, software
  11 +-- distributed under the License is distributed on an "AS IS" BASIS,
  12 +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +-- See the License for the specific language governing permissions and
  14 +-- limitations under the License.
  15 +--
  16 +
  17 +-- load function check_version()
  18 +
  19 +CREATE OR REPLACE FUNCTION check_version() RETURNS boolean AS $$
  20 +DECLARE
  21 + current_version integer;
  22 + valid_version boolean;
  23 +BEGIN
  24 + RAISE NOTICE 'Check the current installed PostgreSQL version...';
  25 + SELECT current_setting('server_version_num') INTO current_version;
  26 + IF current_version < 100000 THEN
  27 + valid_version := FALSE;
  28 + ELSE
  29 + valid_version := TRUE;
  30 + END IF;
  31 + IF valid_version = FALSE THEN
  32 + RAISE NOTICE 'Postgres version should be at least more than 10!';
  33 + ELSE
  34 + RAISE NOTICE 'PostgreSQL version is valid!';
  35 + RAISE NOTICE 'Schema update started...';
  36 + END IF;
  37 + RETURN valid_version;
  38 +END;
  39 +$$ LANGUAGE 'plpgsql';
  40 +
  41 +-- load function create_partition_table()
  42 +
  43 +CREATE OR REPLACE FUNCTION create_partition_table() RETURNS VOID AS $$
  44 +
  45 +BEGIN
  46 + ALTER TABLE ts_kv
  47 + RENAME TO ts_kv_old;
  48 + CREATE TABLE IF NOT EXISTS ts_kv
  49 + (
  50 + LIKE ts_kv_old
  51 + )
  52 + PARTITION BY RANGE (ts);
  53 + ALTER TABLE ts_kv
  54 + DROP COLUMN entity_type;
  55 + ALTER TABLE ts_kv
  56 + ALTER COLUMN entity_id TYPE uuid USING entity_id::uuid;
  57 + ALTER TABLE ts_kv
  58 + ALTER COLUMN key TYPE integer USING key::integer;
  59 +END;
  60 +$$ LANGUAGE 'plpgsql';
  61 +
  62 +
  63 +-- load function create_partitions()
  64 +
  65 +CREATE OR REPLACE FUNCTION create_partitions() RETURNS VOID AS
  66 +$$
  67 +DECLARE
  68 + partition_date varchar;
  69 + from_ts bigint;
  70 + to_ts bigint;
  71 + key_cursor CURSOR FOR select SUBSTRING(month_date.first_date, 1, 7) AS partition_date,
  72 + extract(epoch from (month_date.first_date)::timestamp) * 1000 as from_ts,
  73 + extract(epoch from (month_date.first_date::date + INTERVAL '1 MONTH')::timestamp) *
  74 + 1000 as to_ts
  75 + FROM (SELECT DISTINCT TO_CHAR(TO_TIMESTAMP(ts / 1000), 'YYYY_MM_01') AS first_date
  76 + FROM ts_kv_old) AS month_date;
  77 +BEGIN
  78 + OPEN key_cursor;
  79 + LOOP
  80 + FETCH key_cursor INTO partition_date, from_ts, to_ts;
  81 + EXIT WHEN NOT FOUND;
  82 + EXECUTE 'CREATE TABLE IF NOT EXISTS ts_kv_' || partition_date ||
  83 + ' PARTITION OF ts_kv(PRIMARY KEY (entity_id, key, ts)) FOR VALUES FROM (' || from_ts ||
  84 + ') TO (' || to_ts || ');';
  85 + RAISE NOTICE 'A partition % has been created!',CONCAT('ts_kv_', partition_date);
  86 + END LOOP;
  87 +
  88 + CLOSE key_cursor;
  89 +END;
  90 +$$ language 'plpgsql';
  91 +
  92 +-- load function create_ts_kv_dictionary_table()
  93 +
  94 +CREATE OR REPLACE FUNCTION create_ts_kv_dictionary_table() RETURNS VOID AS $$
  95 +
  96 +BEGIN
  97 + CREATE TABLE IF NOT EXISTS ts_kv_dictionary
  98 + (
  99 + key varchar(255) NOT NULL,
  100 + key_id serial UNIQUE,
  101 + CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
  102 + );
  103 +END;
  104 +$$ LANGUAGE 'plpgsql';
  105 +
  106 +-- load function insert_into_dictionary()
  107 +
  108 +CREATE OR REPLACE FUNCTION insert_into_dictionary() RETURNS VOID AS
  109 +$$
  110 +DECLARE
  111 + insert_record RECORD;
  112 + key_cursor CURSOR FOR SELECT DISTINCT key
  113 + FROM ts_kv_old
  114 + ORDER BY key;
  115 +BEGIN
  116 + OPEN key_cursor;
  117 + LOOP
  118 + FETCH key_cursor INTO insert_record;
  119 + EXIT WHEN NOT FOUND;
  120 + IF NOT EXISTS(SELECT key FROM ts_kv_dictionary WHERE key = insert_record.key) THEN
  121 + INSERT INTO ts_kv_dictionary(key) VALUES (insert_record.key);
  122 + RAISE NOTICE 'Key: % has been inserted into the dictionary!',insert_record.key;
  123 + ELSE
  124 + RAISE NOTICE 'Key: % already exists in the dictionary!',insert_record.key;
  125 + END IF;
  126 + END LOOP;
  127 + CLOSE key_cursor;
  128 +END;
  129 +$$ language 'plpgsql';
  130 +
  131 +-- load function insert_into_ts_kv()
  132 +
  133 +CREATE OR REPLACE FUNCTION insert_into_ts_kv() RETURNS void AS
  134 +$$
  135 +DECLARE
  136 + insert_size CONSTANT integer := 10000;
  137 + insert_counter integer DEFAULT 0;
  138 + insert_record RECORD;
  139 + insert_cursor CURSOR FOR SELECT CONCAT(first, '-', second, '-1', third, '-', fourth, '-', fifth)::uuid AS entity_id,
  140 + substrings.key AS key,
  141 + substrings.ts AS ts,
  142 + substrings.bool_v AS bool_v,
  143 + substrings.str_v AS str_v,
  144 + substrings.long_v AS long_v,
  145 + substrings.dbl_v AS dbl_v
  146 + FROM (SELECT SUBSTRING(entity_id, 8, 8) AS first,
  147 + SUBSTRING(entity_id, 4, 4) AS second,
  148 + SUBSTRING(entity_id, 1, 3) AS third,
  149 + SUBSTRING(entity_id, 16, 4) AS fourth,
  150 + SUBSTRING(entity_id, 20) AS fifth,
  151 + key_id AS key,
  152 + ts,
  153 + bool_v,
  154 + str_v,
  155 + long_v,
  156 + dbl_v
  157 + FROM ts_kv_old
  158 + INNER JOIN ts_kv_dictionary ON (ts_kv_old.key = ts_kv_dictionary.key)) AS substrings;
  159 +BEGIN
  160 + OPEN insert_cursor;
  161 + LOOP
  162 + insert_counter := insert_counter + 1;
  163 + FETCH insert_cursor INTO insert_record;
  164 + IF NOT FOUND THEN
  165 + RAISE NOTICE '% records have been inserted into the partitioned ts_kv!',insert_counter - 1;
  166 + EXIT;
  167 + END IF;
  168 + INSERT INTO ts_kv(entity_id, key, ts, bool_v, str_v, long_v, dbl_v)
  169 + VALUES (insert_record.entity_id, insert_record.key, insert_record.ts, insert_record.bool_v, insert_record.str_v,
  170 + insert_record.long_v, insert_record.dbl_v);
  171 + IF MOD(insert_counter, insert_size) = 0 THEN
  172 + RAISE NOTICE '% records have been inserted into the partitioned ts_kv!',insert_counter;
  173 + END IF;
  174 + END LOOP;
  175 + CLOSE insert_cursor;
  176 +END;
  177 +$$ LANGUAGE 'plpgsql';
  178 +
  179 +
@@ -23,7 +23,8 @@ import org.springframework.context.ApplicationContext; @@ -23,7 +23,8 @@ import org.springframework.context.ApplicationContext;
23 import org.springframework.context.annotation.Profile; 23 import org.springframework.context.annotation.Profile;
24 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
25 import org.thingsboard.server.service.component.ComponentDiscoveryService; 25 import org.thingsboard.server.service.component.ComponentDiscoveryService;
26 -import org.thingsboard.server.service.install.DatabaseUpgradeService; 26 +import org.thingsboard.server.service.install.DatabaseTsUpgradeService;
  27 +import org.thingsboard.server.service.install.DatabaseEntitiesUpgradeService;
27 import org.thingsboard.server.service.install.EntityDatabaseSchemaService; 28 import org.thingsboard.server.service.install.EntityDatabaseSchemaService;
28 import org.thingsboard.server.service.install.SystemDataLoaderService; 29 import org.thingsboard.server.service.install.SystemDataLoaderService;
29 import org.thingsboard.server.service.install.TsDatabaseSchemaService; 30 import org.thingsboard.server.service.install.TsDatabaseSchemaService;
@@ -50,7 +51,10 @@ public class ThingsboardInstallService { @@ -50,7 +51,10 @@ public class ThingsboardInstallService {
50 private TsDatabaseSchemaService tsDatabaseSchemaService; 51 private TsDatabaseSchemaService tsDatabaseSchemaService;
51 52
52 @Autowired 53 @Autowired
53 - private DatabaseUpgradeService databaseUpgradeService; 54 + private DatabaseEntitiesUpgradeService databaseEntitiesUpgradeService;
  55 +
  56 + @Autowired
  57 + private DatabaseTsUpgradeService databaseTsUpgradeService;
54 58
55 @Autowired 59 @Autowired
56 private ComponentDiscoveryService componentDiscoveryService; 60 private ComponentDiscoveryService componentDiscoveryService;
@@ -73,48 +77,48 @@ public class ThingsboardInstallService { @@ -73,48 +77,48 @@ public class ThingsboardInstallService {
73 case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion 77 case "1.2.3": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
74 log.info("Upgrading ThingsBoard from version 1.2.3 to 1.3.0 ..."); 78 log.info("Upgrading ThingsBoard from version 1.2.3 to 1.3.0 ...");
75 79
76 - databaseUpgradeService.upgradeDatabase("1.2.3"); 80 + databaseEntitiesUpgradeService.upgradeDatabase("1.2.3");
77 81
78 case "1.3.0": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion 82 case "1.3.0": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
79 log.info("Upgrading ThingsBoard from version 1.3.0 to 1.3.1 ..."); 83 log.info("Upgrading ThingsBoard from version 1.3.0 to 1.3.1 ...");
80 84
81 - databaseUpgradeService.upgradeDatabase("1.3.0"); 85 + databaseEntitiesUpgradeService.upgradeDatabase("1.3.0");
82 86
83 case "1.3.1": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion 87 case "1.3.1": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
84 log.info("Upgrading ThingsBoard from version 1.3.1 to 1.4.0 ..."); 88 log.info("Upgrading ThingsBoard from version 1.3.1 to 1.4.0 ...");
85 89
86 - databaseUpgradeService.upgradeDatabase("1.3.1"); 90 + databaseEntitiesUpgradeService.upgradeDatabase("1.3.1");
87 91
88 case "1.4.0": 92 case "1.4.0":
89 log.info("Upgrading ThingsBoard from version 1.4.0 to 2.0.0 ..."); 93 log.info("Upgrading ThingsBoard from version 1.4.0 to 2.0.0 ...");
90 94
91 - databaseUpgradeService.upgradeDatabase("1.4.0"); 95 + databaseEntitiesUpgradeService.upgradeDatabase("1.4.0");
92 96
93 dataUpdateService.updateData("1.4.0"); 97 dataUpdateService.updateData("1.4.0");
94 98
95 case "2.0.0": 99 case "2.0.0":
96 log.info("Upgrading ThingsBoard from version 2.0.0 to 2.1.1 ..."); 100 log.info("Upgrading ThingsBoard from version 2.0.0 to 2.1.1 ...");
97 101
98 - databaseUpgradeService.upgradeDatabase("2.0.0"); 102 + databaseEntitiesUpgradeService.upgradeDatabase("2.0.0");
99 103
100 case "2.1.1": 104 case "2.1.1":
101 log.info("Upgrading ThingsBoard from version 2.1.1 to 2.1.2 ..."); 105 log.info("Upgrading ThingsBoard from version 2.1.1 to 2.1.2 ...");
102 106
103 - databaseUpgradeService.upgradeDatabase("2.1.1"); 107 + databaseEntitiesUpgradeService.upgradeDatabase("2.1.1");
104 case "2.1.3": 108 case "2.1.3":
105 log.info("Upgrading ThingsBoard from version 2.1.3 to 2.2.0 ..."); 109 log.info("Upgrading ThingsBoard from version 2.1.3 to 2.2.0 ...");
106 110
107 - databaseUpgradeService.upgradeDatabase("2.1.3"); 111 + databaseEntitiesUpgradeService.upgradeDatabase("2.1.3");
108 112
109 case "2.3.0": 113 case "2.3.0":
110 log.info("Upgrading ThingsBoard from version 2.3.0 to 2.3.1 ..."); 114 log.info("Upgrading ThingsBoard from version 2.3.0 to 2.3.1 ...");
111 115
112 - databaseUpgradeService.upgradeDatabase("2.3.0"); 116 + databaseEntitiesUpgradeService.upgradeDatabase("2.3.0");
113 117
114 case "2.3.1": 118 case "2.3.1":
115 log.info("Upgrading ThingsBoard from version 2.3.1 to 2.4.0 ..."); 119 log.info("Upgrading ThingsBoard from version 2.3.1 to 2.4.0 ...");
116 120
117 - databaseUpgradeService.upgradeDatabase("2.3.1"); 121 + databaseEntitiesUpgradeService.upgradeDatabase("2.3.1");
118 122
119 case "2.4.0": 123 case "2.4.0":
120 log.info("Upgrading ThingsBoard from version 2.4.0 to 2.4.1 ..."); 124 log.info("Upgrading ThingsBoard from version 2.4.0 to 2.4.1 ...");
@@ -122,11 +126,16 @@ public class ThingsboardInstallService { @@ -122,11 +126,16 @@ public class ThingsboardInstallService {
122 case "2.4.1": 126 case "2.4.1":
123 log.info("Upgrading ThingsBoard from version 2.4.1 to 2.4.2 ..."); 127 log.info("Upgrading ThingsBoard from version 2.4.1 to 2.4.2 ...");
124 128
125 - databaseUpgradeService.upgradeDatabase("2.4.1"); 129 + databaseEntitiesUpgradeService.upgradeDatabase("2.4.1");
126 case "2.4.2": 130 case "2.4.2":
127 log.info("Upgrading ThingsBoard from version 2.4.2 to 2.4.3 ..."); 131 log.info("Upgrading ThingsBoard from version 2.4.2 to 2.4.3 ...");
128 132
129 - databaseUpgradeService.upgradeDatabase("2.4.2"); 133 + databaseEntitiesUpgradeService.upgradeDatabase("2.4.2");
  134 +
  135 + case "2.4.3":
  136 + log.info("Upgrading ThingsBoard from version 2.4.3 to 2.5 ...");
  137 +
  138 + databaseTsUpgradeService.upgradeDatabase("2.4.3");
130 139
131 log.info("Updating system data..."); 140 log.info("Updating system data...");
132 141
@@ -59,7 +59,7 @@ import static org.thingsboard.server.service.install.DatabaseHelper.TYPE; @@ -59,7 +59,7 @@ import static org.thingsboard.server.service.install.DatabaseHelper.TYPE;
59 @NoSqlDao 59 @NoSqlDao
60 @Profile("install") 60 @Profile("install")
61 @Slf4j 61 @Slf4j
62 -public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService { 62 +public class CassandraDatabaseUpgradeService implements DatabaseEntitiesUpgradeService {
63 63
64 private static final String SCHEMA_UPDATE_CQL = "schema_update.cql"; 64 private static final String SCHEMA_UPDATE_CQL = "schema_update.cql";
65 65
application/src/main/java/org/thingsboard/server/service/install/DatabaseEntitiesUpgradeService.java renamed from application/src/main/java/org/thingsboard/server/service/install/DatabaseUpgradeService.java
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 */ 15 */
16 package org.thingsboard.server.service.install; 16 package org.thingsboard.server.service.install;
17 17
18 -public interface DatabaseUpgradeService { 18 +public interface DatabaseEntitiesUpgradeService {
19 19
20 void upgradeDatabase(String fromVersion) throws Exception; 20 void upgradeDatabase(String fromVersion) throws Exception;
21 21
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install;
  17 +
  18 +public interface DatabaseTsUpgradeService {
  19 +
  20 + void upgradeDatabase(String fromVersion) throws Exception;
  21 +
  22 +}
application/src/main/java/org/thingsboard/server/service/install/HsqlTsDatabaseSchemaService.java renamed from application/src/main/java/org/thingsboard/server/service/install/SqlTsDatabaseSchemaService.java
@@ -17,14 +17,16 @@ package org.thingsboard.server.service.install; @@ -17,14 +17,16 @@ package org.thingsboard.server.service.install;
17 17
18 import org.springframework.context.annotation.Profile; 18 import org.springframework.context.annotation.Profile;
19 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
  20 +import org.thingsboard.server.dao.util.HsqlDao;
20 import org.thingsboard.server.dao.util.SqlTsDao; 21 import org.thingsboard.server.dao.util.SqlTsDao;
21 22
22 @Service 23 @Service
23 @SqlTsDao 24 @SqlTsDao
  25 +@HsqlDao
24 @Profile("install") 26 @Profile("install")
25 -public class SqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService 27 +public class HsqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
26 implements TsDatabaseSchemaService { 28 implements TsDatabaseSchemaService {
27 - public SqlTsDatabaseSchemaService() {  
28 - super("schema-ts.sql", null); 29 + public HsqlTsDatabaseSchemaService() {
  30 + super("schema-ts-hsql.sql", null);
29 } 31 }
30 } 32 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install;
  17 +
  18 +import org.springframework.context.annotation.Profile;
  19 +import org.springframework.stereotype.Service;
  20 +import org.thingsboard.server.dao.util.PsqlDao;
  21 +import org.thingsboard.server.dao.util.SqlTsDao;
  22 +
  23 +@Service
  24 +@SqlTsDao
  25 +@PsqlDao
  26 +@Profile("install")
  27 +public class PsqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService
  28 + implements TsDatabaseSchemaService {
  29 + public PsqlTsDatabaseSchemaService() {
  30 + super("schema-ts-psql.sql", null);
  31 + }
  32 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.install;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.springframework.beans.factory.annotation.Value;
  21 +import org.springframework.context.annotation.Profile;
  22 +import org.springframework.stereotype.Service;
  23 +import org.thingsboard.server.dao.util.PsqlDao;
  24 +import org.thingsboard.server.dao.util.SqlTsDao;
  25 +
  26 +import java.nio.charset.StandardCharsets;
  27 +import java.nio.file.Files;
  28 +import java.nio.file.Path;
  29 +import java.nio.file.Paths;
  30 +import java.sql.CallableStatement;
  31 +import java.sql.Connection;
  32 +import java.sql.DriverManager;
  33 +import java.sql.SQLException;
  34 +import java.sql.Types;
  35 +
  36 +@Service
  37 +@Profile("install")
  38 +@Slf4j
  39 +@SqlTsDao
  40 +@PsqlDao
  41 +public class PsqlTsDatabaseUpgradeService implements DatabaseTsUpgradeService {
  42 +
  43 + private static final String CALL_REGEX = "call ";
  44 + private static final String LOAD_FUNCTIONS_SQL = "schema_update_psql_ts.sql";
  45 + private static final String CHECK_VERSION = CALL_REGEX + "check_version()";
  46 + private static final String CREATE_PARTITION_TABLE = CALL_REGEX + "create_partition_table()";
  47 + private static final String CREATE_PARTITIONS = CALL_REGEX + "create_partitions()";
  48 + private static final String CREATE_TS_KV_DICTIONARY_TABLE = CALL_REGEX + "create_ts_kv_dictionary_table()";
  49 + private static final String INSERT_INTO_DICTIONARY = CALL_REGEX + "insert_into_dictionary()";
  50 + private static final String INSERT_INTO_TS_KV = CALL_REGEX + "insert_into_ts_kv()";
  51 + private static final String DROP_OLD_TABLE = "DROP TABLE ts_kv_old;";
  52 +
  53 + @Value("${spring.datasource.url}")
  54 + private String dbUrl;
  55 +
  56 + @Value("${spring.datasource.username}")
  57 + private String dbUserName;
  58 +
  59 + @Value("${spring.datasource.password}")
  60 + private String dbPassword;
  61 +
  62 + @Autowired
  63 + private InstallScripts installScripts;
  64 +
  65 + @Override
  66 + public void upgradeDatabase(String fromVersion) throws Exception {
  67 + switch (fromVersion) {
  68 + case "2.4.3":
  69 + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
  70 + log.info("Updating timeseries schema ...");
  71 + log.info("Load upgrade functions ...");
  72 + loadSql(conn);
  73 + log.info("Upgrade functions successfully loaded!");
  74 + boolean versionValid = checkVersion(conn);
  75 + if (!versionValid) {
  76 + log.info("PostgreSQL version should be at least more than 10!");
  77 + log.info("Please upgrade your PostgreSQL and restart the script!");
  78 + } else {
  79 + log.info("PostgreSQL version is valid!");
  80 + log.info("Updating schema ...");
  81 + executeFunction(conn, CREATE_PARTITION_TABLE);
  82 + executeFunction(conn, CREATE_PARTITIONS);
  83 + executeFunction(conn, CREATE_TS_KV_DICTIONARY_TABLE);
  84 + executeFunction(conn, INSERT_INTO_DICTIONARY);
  85 + executeFunction(conn, INSERT_INTO_TS_KV);
  86 + dropOldTable(conn, DROP_OLD_TABLE);
  87 + log.info("schema timeseries updated!");
  88 + }
  89 + }
  90 + break;
  91 + default:
  92 + throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
  93 + }
  94 + }
  95 +
  96 + private void loadSql(Connection conn) {
  97 + Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.4.3", LOAD_FUNCTIONS_SQL);
  98 + try {
  99 + loadFunctions(schemaUpdateFile, conn);
  100 + } catch (Exception e) {
  101 + log.info("Failed to load PostgreSQL upgrade functions due to: {}", e.getMessage());
  102 + }
  103 + }
  104 +
  105 + private void loadFunctions(Path sqlFile, Connection conn) throws Exception {
  106 + String sql = new String(Files.readAllBytes(sqlFile), StandardCharsets.UTF_8);
  107 + conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
  108 + }
  109 +
  110 + private boolean checkVersion(Connection conn) {
  111 + log.info("Check the current PostgreSQL version...");
  112 + boolean versionValid = false;
  113 + try {
  114 + CallableStatement callableStatement = conn.prepareCall("{? = " + CHECK_VERSION + " }");
  115 + callableStatement.registerOutParameter(1, Types.BOOLEAN);
  116 + callableStatement.execute();
  117 + versionValid = callableStatement.getBoolean(1);
  118 + callableStatement.close();
  119 + } catch (Exception e) {
  120 + log.info("Failed to check current PostgreSQL version due to: {}", e.getMessage());
  121 + }
  122 + return versionValid;
  123 + }
  124 +
  125 + private void executeFunction(Connection conn, String query) {
  126 + log.info("{} ... ", query);
  127 + try {
  128 + CallableStatement callableStatement = conn.prepareCall("{" + query + "}");
  129 + callableStatement.execute();
  130 + callableStatement.close();
  131 + log.info("Successfully executed: {}", query.replace(CALL_REGEX, ""));
  132 + } catch (Exception e) {
  133 + log.info("Failed to execute {} due to: {}", query, e.getMessage());
  134 + }
  135 + }
  136 +
  137 + private void dropOldTable(Connection conn, String query) {
  138 + try {
  139 + conn.createStatement().execute(query); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
  140 + Thread.sleep(5000);
  141 + } catch (InterruptedException | SQLException e) {
  142 + log.info("Failed to drop table {} due to: {}", query.replace("DROP TABLE ", ""), e.getMessage());
  143 + }
  144 + }
  145 +}
@@ -54,7 +54,7 @@ import static org.thingsboard.server.service.install.DatabaseHelper.TYPE; @@ -54,7 +54,7 @@ import static org.thingsboard.server.service.install.DatabaseHelper.TYPE;
54 @Profile("install") 54 @Profile("install")
55 @Slf4j 55 @Slf4j
56 @SqlDao 56 @SqlDao
57 -public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { 57 +public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService {
58 58
59 private static final String SCHEMA_UPDATE_SQL = "schema_update.sql"; 59 private static final String SCHEMA_UPDATE_SQL = "schema_update.sql";
60 60
@@ -172,7 +172,8 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { @@ -172,7 +172,8 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
172 loadSql(schemaUpdateFile, conn); 172 loadSql(schemaUpdateFile, conn);
173 try { 173 try {
174 conn.createStatement().execute("ALTER TABLE device ADD COLUMN label varchar(255)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script 174 conn.createStatement().execute("ALTER TABLE device ADD COLUMN label varchar(255)"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
175 - } catch (Exception e) {} 175 + } catch (Exception e) {
  176 + }
176 log.info("Schema updated."); 177 log.info("Schema updated.");
177 } 178 }
178 break; 179 break;
@@ -201,7 +202,8 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService { @@ -201,7 +202,8 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
201 log.info("Updating schema ..."); 202 log.info("Updating schema ...");
202 try { 203 try {
203 conn.createStatement().execute("ALTER TABLE alarm ADD COLUMN propagate_relation_types varchar"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script 204 conn.createStatement().execute("ALTER TABLE alarm ADD COLUMN propagate_relation_types varchar"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
204 - } catch (Exception e) {} 205 + } catch (Exception e) {
  206 + }
205 log.info("Schema updated."); 207 log.info("Schema updated.");
206 } 208 }
207 break; 209 break;
@@ -171,7 +171,7 @@ cassandra: @@ -171,7 +171,7 @@ cassandra:
171 read_consistency_level: "${CASSANDRA_READ_CONSISTENCY_LEVEL:ONE}" 171 read_consistency_level: "${CASSANDRA_READ_CONSISTENCY_LEVEL:ONE}"
172 write_consistency_level: "${CASSANDRA_WRITE_CONSISTENCY_LEVEL:ONE}" 172 write_consistency_level: "${CASSANDRA_WRITE_CONSISTENCY_LEVEL:ONE}"
173 default_fetch_size: "${CASSANDRA_DEFAULT_FETCH_SIZE:2000}" 173 default_fetch_size: "${CASSANDRA_DEFAULT_FETCH_SIZE:2000}"
174 - # Specify partitioning size for timestamp key-value storage. Example MINUTES, HOURS, DAYS, MONTHS,INDEFINITE 174 + # Specify partitioning size for timestamp key-value storage. Example: MINUTES, HOURS, DAYS, MONTHS,INDEFINITE
175 ts_key_value_partitioning: "${TS_KV_PARTITIONING:MONTHS}" 175 ts_key_value_partitioning: "${TS_KV_PARTITIONING:MONTHS}"
176 ts_key_value_ttl: "${TS_KV_TTL:0}" 176 ts_key_value_ttl: "${TS_KV_TTL:0}"
177 events_ttl: "${TS_EVENTS_TTL:0}" 177 events_ttl: "${TS_EVENTS_TTL:0}"
@@ -214,6 +214,8 @@ sql: @@ -214,6 +214,8 @@ sql:
214 stats_print_interval_ms: "${SQL_TS_TIMESCALE_BATCH_STATS_PRINT_MS:10000}" 214 stats_print_interval_ms: "${SQL_TS_TIMESCALE_BATCH_STATS_PRINT_MS:10000}"
215 # Specify whether to remove null characters from strValue of attributes and timeseries before insert 215 # Specify whether to remove null characters from strValue of attributes and timeseries before insert
216 remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}" 216 remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}"
  217 + # Specify partitioning size for timestamp key-value storage. Example: DAYS, MONTHS, YEARS, INDEFINITE
  218 + ts_key_value_partitioning: "${TS_KV_PARTITIONING:MONTHS}"
217 219
218 # Actor system parameters 220 # Actor system parameters
219 actors: 221 actors:
@@ -30,7 +30,7 @@ public class ControllerSqlTestSuite { @@ -30,7 +30,7 @@ public class ControllerSqlTestSuite {
30 30
31 @ClassRule 31 @ClassRule
32 public static CustomSqlUnit sqlUnit = new CustomSqlUnit( 32 public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
33 - Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"), 33 + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"),
34 "sql/drop-all-tables.sql", 34 "sql/drop-all-tables.sql",
35 "sql-test.properties"); 35 "sql-test.properties");
36 } 36 }
@@ -29,7 +29,7 @@ public class MqttSqlTestSuite { @@ -29,7 +29,7 @@ public class MqttSqlTestSuite {
29 29
30 @ClassRule 30 @ClassRule
31 public static CustomSqlUnit sqlUnit = new CustomSqlUnit( 31 public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
32 - Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), 32 + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"),
33 "sql/drop-all-tables.sql", 33 "sql/drop-all-tables.sql",
34 "sql-test.properties"); 34 "sql-test.properties");
35 } 35 }
@@ -30,7 +30,7 @@ public class RuleEngineSqlTestSuite { @@ -30,7 +30,7 @@ public class RuleEngineSqlTestSuite {
30 30
31 @ClassRule 31 @ClassRule
32 public static CustomSqlUnit sqlUnit = new CustomSqlUnit( 32 public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
33 - Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), 33 + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"),
34 "sql/drop-all-tables.sql", 34 "sql/drop-all-tables.sql",
35 "sql-test.properties"); 35 "sql-test.properties");
36 } 36 }
@@ -31,7 +31,7 @@ public class SystemSqlTestSuite { @@ -31,7 +31,7 @@ public class SystemSqlTestSuite {
31 31
32 @ClassRule 32 @ClassRule
33 public static CustomSqlUnit sqlUnit = new CustomSqlUnit( 33 public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
34 - Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), 34 + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"),
35 "sql/drop-all-tables.sql", 35 "sql/drop-all-tables.sql",
36 "sql-test.properties"); 36 "sql-test.properties");
37 37
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.util;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
  19 +
  20 +@ConditionalOnExpression("('${database.ts.type}'=='sql' || '${database.entities.type}'=='timescale') " +
  21 + "&& '${spring.jpa.database-platform}'=='org.hibernate.dialect.PostgreSQLDialect'")
  22 +public @interface PsqlTsAnyDao {
  23 +}
dao/src/main/java/org/thingsboard/server/dao/HsqlTsDaoConfig.java renamed from dao/src/main/java/org/thingsboard/server/dao/SqlTsDaoConfig.java
@@ -21,15 +21,17 @@ import org.springframework.context.annotation.ComponentScan; @@ -21,15 +21,17 @@ import org.springframework.context.annotation.ComponentScan;
21 import org.springframework.context.annotation.Configuration; 21 import org.springframework.context.annotation.Configuration;
22 import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 22 import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
23 import org.springframework.transaction.annotation.EnableTransactionManagement; 23 import org.springframework.transaction.annotation.EnableTransactionManagement;
  24 +import org.thingsboard.server.dao.util.HsqlDao;
24 import org.thingsboard.server.dao.util.SqlTsDao; 25 import org.thingsboard.server.dao.util.SqlTsDao;
25 26
26 @Configuration 27 @Configuration
27 @EnableAutoConfiguration 28 @EnableAutoConfiguration
28 -@ComponentScan("org.thingsboard.server.dao.sqlts.ts")  
29 -@EnableJpaRepositories("org.thingsboard.server.dao.sqlts.ts")  
30 -@EntityScan("org.thingsboard.server.dao.model.sqlts.ts") 29 +@ComponentScan({"org.thingsboard.server.dao.sqlts.hsql", "org.thingsboard.server.dao.sqlts.latest"})
  30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.hsql", "org.thingsboard.server.dao.sqlts.latest"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.hsql", "org.thingsboard.server.dao.model.sqlts.latest"})
31 @EnableTransactionManagement 32 @EnableTransactionManagement
32 @SqlTsDao 33 @SqlTsDao
33 -public class SqlTsDaoConfig { 34 +@HsqlDao
  35 +public class HsqlTsDaoConfig {
34 36
35 } 37 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao;
  17 +
  18 +import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
  19 +import org.springframework.boot.autoconfigure.domain.EntityScan;
  20 +import org.springframework.context.annotation.ComponentScan;
  21 +import org.springframework.context.annotation.Configuration;
  22 +import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  23 +import org.springframework.transaction.annotation.EnableTransactionManagement;
  24 +import org.thingsboard.server.dao.util.PsqlDao;
  25 +import org.thingsboard.server.dao.util.SqlTsDao;
  26 +
  27 +@Configuration
  28 +@EnableAutoConfiguration
  29 +@ComponentScan({"org.thingsboard.server.dao.sqlts.psql", "org.thingsboard.server.dao.sqlts.latest"})
  30 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.psql", "org.thingsboard.server.dao.sqlts.latest", "org.thingsboard.server.dao.sqlts.dictionary"})
  31 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.psql", "org.thingsboard.server.dao.model.sqlts.latest", "org.thingsboard.server.dao.model.sqlts.dictionary"})
  32 +@EnableTransactionManagement
  33 +@SqlTsDao
  34 +@PsqlDao
  35 +public class PsqlTsDaoConfig {
  36 +
  37 +}
@@ -25,9 +25,9 @@ import org.thingsboard.server.dao.util.TimescaleDBTsDao; @@ -25,9 +25,9 @@ import org.thingsboard.server.dao.util.TimescaleDBTsDao;
25 25
26 @Configuration 26 @Configuration
27 @EnableAutoConfiguration 27 @EnableAutoConfiguration
28 -@ComponentScan("org.thingsboard.server.dao.sqlts.timescale")  
29 -@EnableJpaRepositories("org.thingsboard.server.dao.sqlts.timescale")  
30 -@EntityScan("org.thingsboard.server.dao.model.sqlts.timescale") 28 +@ComponentScan({"org.thingsboard.server.dao.sqlts.timescale", "org.thingsboard.server.dao.sqlts.latest"})
  29 +@EnableJpaRepositories({"org.thingsboard.server.dao.sqlts.timescale", "org.thingsboard.server.dao.sqlts.dictionary", "org.thingsboard.server.dao.sqlts.latest"})
  30 +@EntityScan({"org.thingsboard.server.dao.model.sqlts.timescale", "org.thingsboard.server.dao.model.sqlts.dictionary", "org.thingsboard.server.dao.model.sqlts.latest"})
31 @EnableTransactionManagement 31 @EnableTransactionManagement
32 @TimescaleDBTsDao 32 @TimescaleDBTsDao
33 public class TimescaleDaoConfig { 33 public class TimescaleDaoConfig {
@@ -41,7 +41,7 @@ import org.thingsboard.server.dao.DaoUtil; @@ -41,7 +41,7 @@ import org.thingsboard.server.dao.DaoUtil;
41 import org.thingsboard.server.dao.model.ModelConstants; 41 import org.thingsboard.server.dao.model.ModelConstants;
42 import org.thingsboard.server.dao.model.nosql.AuditLogEntity; 42 import org.thingsboard.server.dao.model.nosql.AuditLogEntity;
43 import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTimeDao; 43 import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTimeDao;
44 -import org.thingsboard.server.dao.timeseries.TsPartitionDate; 44 +import org.thingsboard.server.dao.timeseries.NoSqlTsPartitionDate;
45 import org.thingsboard.server.dao.util.NoSqlDao; 45 import org.thingsboard.server.dao.util.NoSqlDao;
46 46
47 import javax.annotation.Nullable; 47 import javax.annotation.Nullable;
@@ -92,7 +92,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo @@ -92,7 +92,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
92 92
93 @Value("${audit-log.by_tenant_partitioning}") 93 @Value("${audit-log.by_tenant_partitioning}")
94 private String partitioning; 94 private String partitioning;
95 - private TsPartitionDate tsFormat; 95 + private NoSqlTsPartitionDate tsFormat;
96 96
97 @Value("${audit-log.default_query_period}") 97 @Value("${audit-log.default_query_period}")
98 private Integer defaultQueryPeriodInDays; 98 private Integer defaultQueryPeriodInDays;
@@ -110,7 +110,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo @@ -110,7 +110,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
110 @PostConstruct 110 @PostConstruct
111 public void init() { 111 public void init() {
112 if (!isInstall()) { 112 if (!isInstall()) {
113 - Optional<TsPartitionDate> partition = TsPartitionDate.parse(partitioning); 113 + Optional<NoSqlTsPartitionDate> partition = NoSqlTsPartitionDate.parse(partitioning);
114 if (partition.isPresent()) { 114 if (partition.isPresent()) {
115 tsFormat = partition.get(); 115 tsFormat = partition.get();
116 } else { 116 } else {
@@ -359,6 +359,7 @@ public class ModelConstants { @@ -359,6 +359,7 @@ public class ModelConstants {
359 359
360 public static final String PARTITION_COLUMN = "partition"; 360 public static final String PARTITION_COLUMN = "partition";
361 public static final String KEY_COLUMN = "key"; 361 public static final String KEY_COLUMN = "key";
  362 + public static final String KEY_ID_COLUMN = "key_id";
362 public static final String TS_COLUMN = "ts"; 363 public static final String TS_COLUMN = "ts";
363 364
364 /** 365 /**
@@ -16,11 +16,6 @@ @@ -16,11 +16,6 @@
16 package org.thingsboard.server.dao.model.sql; 16 package org.thingsboard.server.dao.model.sql;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 -import org.thingsboard.server.common.data.kv.BooleanDataEntry;  
20 -import org.thingsboard.server.common.data.kv.DoubleDataEntry;  
21 -import org.thingsboard.server.common.data.kv.KvEntry;  
22 -import org.thingsboard.server.common.data.kv.LongDataEntry;  
23 -import org.thingsboard.server.common.data.kv.StringDataEntry;  
24 19
25 import javax.persistence.Column; 20 import javax.persistence.Column;
26 import javax.persistence.Id; 21 import javax.persistence.Id;
@@ -28,10 +23,9 @@ import javax.persistence.MappedSuperclass; @@ -28,10 +23,9 @@ import javax.persistence.MappedSuperclass;
28 23
29 import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; 24 import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN;
30 import static org.thingsboard.server.dao.model.ModelConstants.DOUBLE_VALUE_COLUMN; 25 import static org.thingsboard.server.dao.model.ModelConstants.DOUBLE_VALUE_COLUMN;
31 -import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN;  
32 -import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN;  
33 import static org.thingsboard.server.dao.model.ModelConstants.LONG_VALUE_COLUMN; 26 import static org.thingsboard.server.dao.model.ModelConstants.LONG_VALUE_COLUMN;
34 import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUMN; 27 import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUMN;
  28 +import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN;
35 29
36 @Data 30 @Data
37 @MappedSuperclass 31 @MappedSuperclass
@@ -43,12 +37,8 @@ public abstract class AbstractTsKvEntity { @@ -43,12 +37,8 @@ public abstract class AbstractTsKvEntity {
43 protected static final String MAX = "MAX"; 37 protected static final String MAX = "MAX";
44 38
45 @Id 39 @Id
46 - @Column(name = ENTITY_ID_COLUMN)  
47 - protected String entityId;  
48 -  
49 - @Id  
50 - @Column(name = KEY_COLUMN)  
51 - protected String key; 40 + @Column(name = TS_COLUMN)
  41 + protected Long ts;
52 42
53 @Column(name = BOOLEAN_VALUE_COLUMN) 43 @Column(name = BOOLEAN_VALUE_COLUMN)
54 protected Boolean booleanValue; 44 protected Boolean booleanValue;
@@ -62,20 +52,6 @@ public abstract class AbstractTsKvEntity { @@ -62,20 +52,6 @@ public abstract class AbstractTsKvEntity {
62 @Column(name = DOUBLE_VALUE_COLUMN) 52 @Column(name = DOUBLE_VALUE_COLUMN)
63 protected Double doubleValue; 53 protected Double doubleValue;
64 54
65 - protected KvEntry getKvEntry() {  
66 - KvEntry kvEntry = null;  
67 - if (strValue != null) {  
68 - kvEntry = new StringDataEntry(key, strValue);  
69 - } else if (longValue != null) {  
70 - kvEntry = new LongDataEntry(key, longValue);  
71 - } else if (doubleValue != null) {  
72 - kvEntry = new DoubleDataEntry(key, doubleValue);  
73 - } else if (booleanValue != null) {  
74 - kvEntry = new BooleanDataEntry(key, booleanValue);  
75 - }  
76 - return kvEntry;  
77 - }  
78 -  
79 public abstract boolean isNotEmpty(); 55 public abstract boolean isNotEmpty();
80 56
81 protected static boolean isAllNull(Object... args) { 57 protected static boolean isAllNull(Object... args) {
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.sqlts.dictionary;
  17 +
  18 +import lombok.Data;
  19 +import org.hibernate.annotations.Generated;
  20 +import org.hibernate.annotations.GenerationTime;
  21 +
  22 +import javax.persistence.Column;
  23 +import javax.persistence.Entity;
  24 +import javax.persistence.Id;
  25 +import javax.persistence.IdClass;
  26 +import javax.persistence.Table;
  27 +
  28 +import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN;
  29 +import static org.thingsboard.server.dao.model.ModelConstants.KEY_ID_COLUMN;
  30 +
  31 +@Data
  32 +@Entity
  33 +@Table(name = "ts_kv_dictionary")
  34 +@IdClass(TsKvDictionaryCompositeKey.class)
  35 +public final class TsKvDictionary {
  36 +
  37 + @Id
  38 + @Column(name = KEY_COLUMN)
  39 + private String key;
  40 +
  41 + @Column(name = KEY_ID_COLUMN, unique = true, columnDefinition="serial")
  42 + @Generated(GenerationTime.INSERT)
  43 + private int keyId;
  44 +
  45 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.sqlts.dictionary;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import lombok.NoArgsConstructor;
  21 +
  22 +import javax.persistence.Transient;
  23 +import java.io.Serializable;
  24 +
  25 +@Data
  26 +@NoArgsConstructor
  27 +@AllArgsConstructor
  28 +public class TsKvDictionaryCompositeKey implements Serializable{
  29 +
  30 + @Transient
  31 + private static final long serialVersionUID = -4089175869616037523L;
  32 +
  33 + private String key;
  34 +}
dao/src/main/java/org/thingsboard/server/dao/model/sqlts/hsql/TsKvCompositeKey.java renamed from dao/src/main/java/org/thingsboard/server/dao/model/sqlts/ts/TsKvCompositeKey.java
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.dao.model.sqlts.ts; 16 +package org.thingsboard.server.dao.model.sqlts.hsql;
17 17
18 import lombok.AllArgsConstructor; 18 import lombok.AllArgsConstructor;
19 import lombok.Data; 19 import lombok.Data;
@@ -35,4 +35,5 @@ public class TsKvCompositeKey implements Serializable { @@ -35,4 +35,5 @@ public class TsKvCompositeKey implements Serializable {
35 private String entityId; 35 private String entityId;
36 private String key; 36 private String key;
37 private long ts; 37 private long ts;
  38 +
38 } 39 }
dao/src/main/java/org/thingsboard/server/dao/model/sqlts/hsql/TsKvEntity.java renamed from dao/src/main/java/org/thingsboard/server/dao/model/sqlts/ts/TsKvEntity.java
@@ -13,11 +13,16 @@ @@ -13,11 +13,16 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.dao.model.sqlts.ts; 16 +package org.thingsboard.server.dao.model.sqlts.hsql;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 import org.thingsboard.server.common.data.EntityType; 19 import org.thingsboard.server.common.data.EntityType;
20 import org.thingsboard.server.common.data.kv.BasicTsKvEntry; 20 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  21 +import org.thingsboard.server.common.data.kv.BooleanDataEntry;
  22 +import org.thingsboard.server.common.data.kv.DoubleDataEntry;
  23 +import org.thingsboard.server.common.data.kv.KvEntry;
  24 +import org.thingsboard.server.common.data.kv.LongDataEntry;
  25 +import org.thingsboard.server.common.data.kv.StringDataEntry;
21 import org.thingsboard.server.common.data.kv.TsKvEntry; 26 import org.thingsboard.server.common.data.kv.TsKvEntry;
22 import org.thingsboard.server.dao.model.ToData; 27 import org.thingsboard.server.dao.model.ToData;
23 import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; 28 import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity;
@@ -30,8 +35,9 @@ import javax.persistence.Id; @@ -30,8 +35,9 @@ import javax.persistence.Id;
30 import javax.persistence.IdClass; 35 import javax.persistence.IdClass;
31 import javax.persistence.Table; 36 import javax.persistence.Table;
32 37
  38 +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN;
33 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN; 39 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN;
34 -import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN; 40 +import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN;
35 41
36 @Data 42 @Data
37 @Entity 43 @Entity
@@ -45,8 +51,12 @@ public final class TsKvEntity extends AbstractTsKvEntity implements ToData<TsKvE @@ -45,8 +51,12 @@ public final class TsKvEntity extends AbstractTsKvEntity implements ToData<TsKvE
45 private EntityType entityType; 51 private EntityType entityType;
46 52
47 @Id 53 @Id
48 - @Column(name = TS_COLUMN)  
49 - protected Long ts; 54 + @Column(name = ENTITY_ID_COLUMN)
  55 + private String entityId;
  56 +
  57 + @Id
  58 + @Column(name = KEY_COLUMN)
  59 + private String key;
50 60
51 public TsKvEntity() { 61 public TsKvEntity() {
52 } 62 }
@@ -113,6 +123,16 @@ public final class TsKvEntity extends AbstractTsKvEntity implements ToData<TsKvE @@ -113,6 +123,16 @@ public final class TsKvEntity extends AbstractTsKvEntity implements ToData<TsKvE
113 123
114 @Override 124 @Override
115 public TsKvEntry toData() { 125 public TsKvEntry toData() {
116 - return new BasicTsKvEntry(ts, getKvEntry()); 126 + KvEntry kvEntry = null;
  127 + if (strValue != null) {
  128 + kvEntry = new StringDataEntry(key, strValue);
  129 + } else if (longValue != null) {
  130 + kvEntry = new LongDataEntry(key, longValue);
  131 + } else if (doubleValue != null) {
  132 + kvEntry = new DoubleDataEntry(key, doubleValue);
  133 + } else if (booleanValue != null) {
  134 + kvEntry = new BooleanDataEntry(key, booleanValue);
  135 + }
  136 + return new BasicTsKvEntry(ts, kvEntry);
117 } 137 }
118 -} 138 +}
dao/src/main/java/org/thingsboard/server/dao/model/sqlts/latest/TsKvLatestCompositeKey.java renamed from dao/src/main/java/org/thingsboard/server/dao/model/sqlts/ts/TsKvLatestCompositeKey.java
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.dao.model.sqlts.ts; 16 +package org.thingsboard.server.dao.model.sqlts.latest;
17 17
18 import lombok.AllArgsConstructor; 18 import lombok.AllArgsConstructor;
19 import lombok.Data; 19 import lombok.Data;
dao/src/main/java/org/thingsboard/server/dao/model/sqlts/latest/TsKvLatestEntity.java renamed from dao/src/main/java/org/thingsboard/server/dao/model/sqlts/ts/TsKvLatestEntity.java
@@ -13,11 +13,16 @@ @@ -13,11 +13,16 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.dao.model.sqlts.ts; 16 +package org.thingsboard.server.dao.model.sqlts.latest;
17 17
18 import lombok.Data; 18 import lombok.Data;
19 import org.thingsboard.server.common.data.EntityType; 19 import org.thingsboard.server.common.data.EntityType;
20 import org.thingsboard.server.common.data.kv.BasicTsKvEntry; 20 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  21 +import org.thingsboard.server.common.data.kv.BooleanDataEntry;
  22 +import org.thingsboard.server.common.data.kv.DoubleDataEntry;
  23 +import org.thingsboard.server.common.data.kv.KvEntry;
  24 +import org.thingsboard.server.common.data.kv.LongDataEntry;
  25 +import org.thingsboard.server.common.data.kv.StringDataEntry;
21 import org.thingsboard.server.common.data.kv.TsKvEntry; 26 import org.thingsboard.server.common.data.kv.TsKvEntry;
22 import org.thingsboard.server.dao.model.ToData; 27 import org.thingsboard.server.dao.model.ToData;
23 import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; 28 import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity;
@@ -30,8 +35,9 @@ import javax.persistence.Id; @@ -30,8 +35,9 @@ import javax.persistence.Id;
30 import javax.persistence.IdClass; 35 import javax.persistence.IdClass;
31 import javax.persistence.Table; 36 import javax.persistence.Table;
32 37
  38 +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN;
33 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN; 39 import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN;
34 -import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN; 40 +import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN;
35 41
36 @Data 42 @Data
37 @Entity 43 @Entity
@@ -44,16 +50,32 @@ public final class TsKvLatestEntity extends AbstractTsKvEntity implements ToData @@ -44,16 +50,32 @@ public final class TsKvLatestEntity extends AbstractTsKvEntity implements ToData
44 @Column(name = ENTITY_TYPE_COLUMN) 50 @Column(name = ENTITY_TYPE_COLUMN)
45 private EntityType entityType; 51 private EntityType entityType;
46 52
47 - @Column(name = TS_COLUMN)  
48 - private long ts; 53 + @Id
  54 + @Column(name = ENTITY_ID_COLUMN)
  55 + private String entityId;
49 56
50 - @Override  
51 - public TsKvEntry toData() {  
52 - return new BasicTsKvEntry(ts, getKvEntry());  
53 - } 57 + @Id
  58 + @Column(name = KEY_COLUMN)
  59 + private String key;
54 60
55 @Override 61 @Override
56 public boolean isNotEmpty() { 62 public boolean isNotEmpty() {
57 return strValue != null || longValue != null || doubleValue != null || booleanValue != null; 63 return strValue != null || longValue != null || doubleValue != null || booleanValue != null;
58 } 64 }
  65 +
  66 + @Override
  67 + public TsKvEntry toData() {
  68 + KvEntry kvEntry = null;
  69 + if (strValue != null) {
  70 + kvEntry = new StringDataEntry(key, strValue);
  71 + } else if (longValue != null) {
  72 + kvEntry = new LongDataEntry(key, longValue);
  73 + } else if (doubleValue != null) {
  74 + kvEntry = new DoubleDataEntry(key, doubleValue);
  75 + } else if (booleanValue != null) {
  76 + kvEntry = new BooleanDataEntry(key, booleanValue);
  77 + }
  78 + return new BasicTsKvEntry(ts, kvEntry);
  79 + }
  80 +
59 } 81 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.sqlts.psql;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import lombok.NoArgsConstructor;
  21 +
  22 +import javax.persistence.Transient;
  23 +import java.io.Serializable;
  24 +import java.util.UUID;
  25 +
  26 +@Data
  27 +@AllArgsConstructor
  28 +@NoArgsConstructor
  29 +public class TsKvCompositeKey implements Serializable {
  30 +
  31 + @Transient
  32 + private static final long serialVersionUID = -4089175869616037523L;
  33 +
  34 + private UUID entityId;
  35 + private int key;
  36 + private long ts;
  37 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.model.sqlts.psql;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  20 +import org.thingsboard.server.common.data.kv.BooleanDataEntry;
  21 +import org.thingsboard.server.common.data.kv.DoubleDataEntry;
  22 +import org.thingsboard.server.common.data.kv.KvEntry;
  23 +import org.thingsboard.server.common.data.kv.LongDataEntry;
  24 +import org.thingsboard.server.common.data.kv.StringDataEntry;
  25 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  26 +import org.thingsboard.server.dao.model.ToData;
  27 +import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity;
  28 +
  29 +import javax.persistence.Column;
  30 +import javax.persistence.Entity;
  31 +import javax.persistence.Id;
  32 +import javax.persistence.IdClass;
  33 +import javax.persistence.Table;
  34 +import javax.persistence.Transient;
  35 +import java.util.UUID;
  36 +
  37 +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN;
  38 +import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN;
  39 +
  40 +@Data
  41 +@Entity
  42 +@Table(name = "ts_kv")
  43 +@IdClass(TsKvCompositeKey.class)
  44 +public final class TsKvEntity extends AbstractTsKvEntity implements ToData<TsKvEntry> {
  45 +
  46 + @Id
  47 + @Column(name = ENTITY_ID_COLUMN, columnDefinition = "uuid")
  48 + protected UUID entityId;
  49 +
  50 + @Id
  51 + @Column(name = KEY_COLUMN)
  52 + protected int key;
  53 +
  54 + @Transient
  55 + protected String strKey;
  56 +
  57 + public TsKvEntity() {
  58 + }
  59 +
  60 + public TsKvEntity(String strValue) {
  61 + this.strValue = strValue;
  62 + }
  63 +
  64 + public TsKvEntity(Long longValue, Double doubleValue, Long longCountValue, Long doubleCountValue, String aggType) {
  65 + if (!isAllNull(longValue, doubleValue, longCountValue, doubleCountValue)) {
  66 + switch (aggType) {
  67 + case AVG:
  68 + double sum = 0.0;
  69 + if (longValue != null) {
  70 + sum += longValue;
  71 + }
  72 + if (doubleValue != null) {
  73 + sum += doubleValue;
  74 + }
  75 + long totalCount = longCountValue + doubleCountValue;
  76 + if (totalCount > 0) {
  77 + this.doubleValue = sum / (longCountValue + doubleCountValue);
  78 + } else {
  79 + this.doubleValue = 0.0;
  80 + }
  81 + break;
  82 + case SUM:
  83 + if (doubleCountValue > 0) {
  84 + this.doubleValue = doubleValue + (longValue != null ? longValue.doubleValue() : 0.0);
  85 + } else {
  86 + this.longValue = longValue;
  87 + }
  88 + break;
  89 + case MIN:
  90 + case MAX:
  91 + if (longCountValue > 0 && doubleCountValue > 0) {
  92 + this.doubleValue = MAX.equals(aggType) ? Math.max(doubleValue, longValue.doubleValue()) : Math.min(doubleValue, longValue.doubleValue());
  93 + } else if (doubleCountValue > 0) {
  94 + this.doubleValue = doubleValue;
  95 + } else if (longCountValue > 0) {
  96 + this.longValue = longValue;
  97 + }
  98 + break;
  99 + }
  100 + }
  101 + }
  102 +
  103 + public TsKvEntity(Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount) {
  104 + if (!isAllNull(booleanValueCount, strValueCount, longValueCount, doubleValueCount)) {
  105 + if (booleanValueCount != 0) {
  106 + this.longValue = booleanValueCount;
  107 + } else if (strValueCount != 0) {
  108 + this.longValue = strValueCount;
  109 + } else {
  110 + this.longValue = longValueCount + doubleValueCount;
  111 + }
  112 + }
  113 + }
  114 +
  115 + @Override
  116 + public boolean isNotEmpty() {
  117 + return strValue != null || longValue != null || doubleValue != null || booleanValue != null;
  118 + }
  119 +
  120 + @Override
  121 + public TsKvEntry toData() {
  122 + KvEntry kvEntry = null;
  123 + if (strValue != null) {
  124 + kvEntry = new StringDataEntry(strKey, strValue);
  125 + } else if (longValue != null) {
  126 + kvEntry = new LongDataEntry(strKey, longValue);
  127 + } else if (doubleValue != null) {
  128 + kvEntry = new DoubleDataEntry(strKey, doubleValue);
  129 + } else if (booleanValue != null) {
  130 + kvEntry = new BooleanDataEntry(strKey, booleanValue);
  131 + }
  132 + return new BasicTsKvEntry(ts, kvEntry);
  133 + }
  134 +
  135 +}
@@ -21,6 +21,7 @@ import lombok.NoArgsConstructor; @@ -21,6 +21,7 @@ import lombok.NoArgsConstructor;
21 21
22 import javax.persistence.Transient; 22 import javax.persistence.Transient;
23 import java.io.Serializable; 23 import java.io.Serializable;
  24 +import java.util.UUID;
24 25
25 @Data 26 @Data
26 @AllArgsConstructor 27 @AllArgsConstructor
@@ -30,8 +31,8 @@ public class TimescaleTsKvCompositeKey implements Serializable { @@ -30,8 +31,8 @@ public class TimescaleTsKvCompositeKey implements Serializable {
30 @Transient 31 @Transient
31 private static final long serialVersionUID = -4089175869616037523L; 32 private static final long serialVersionUID = -4089175869616037523L;
32 33
33 - private String tenantId;  
34 - private String entityId;  
35 - private String key; 34 + private UUID tenantId;
  35 + private UUID entityId;
  36 + private int key;
36 private long ts; 37 private long ts;
37 } 38 }
@@ -19,6 +19,11 @@ import lombok.Data; @@ -19,6 +19,11 @@ import lombok.Data;
19 import lombok.EqualsAndHashCode; 19 import lombok.EqualsAndHashCode;
20 import org.springframework.util.StringUtils; 20 import org.springframework.util.StringUtils;
21 import org.thingsboard.server.common.data.kv.BasicTsKvEntry; 21 import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
  22 +import org.thingsboard.server.common.data.kv.BooleanDataEntry;
  23 +import org.thingsboard.server.common.data.kv.DoubleDataEntry;
  24 +import org.thingsboard.server.common.data.kv.KvEntry;
  25 +import org.thingsboard.server.common.data.kv.LongDataEntry;
  26 +import org.thingsboard.server.common.data.kv.StringDataEntry;
22 import org.thingsboard.server.common.data.kv.TsKvEntry; 27 import org.thingsboard.server.common.data.kv.TsKvEntry;
23 import org.thingsboard.server.dao.model.ToData; 28 import org.thingsboard.server.dao.model.ToData;
24 import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; 29 import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity;
@@ -34,9 +39,12 @@ import javax.persistence.NamedNativeQuery; @@ -34,9 +39,12 @@ import javax.persistence.NamedNativeQuery;
34 import javax.persistence.SqlResultSetMapping; 39 import javax.persistence.SqlResultSetMapping;
35 import javax.persistence.SqlResultSetMappings; 40 import javax.persistence.SqlResultSetMappings;
36 import javax.persistence.Table; 41 import javax.persistence.Table;
  42 +import javax.persistence.Transient;
  43 +import java.util.UUID;
37 44
  45 +import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN;
  46 +import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN;
38 import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN; 47 import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN;
39 -import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN;  
40 import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.FIND_AVG; 48 import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.FIND_AVG;
41 import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.FIND_AVG_QUERY; 49 import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.FIND_AVG_QUERY;
42 import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.FIND_COUNT; 50 import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.FIND_COUNT;
@@ -118,21 +126,30 @@ import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.F @@ -118,21 +126,30 @@ import static org.thingsboard.server.dao.sqlts.timescale.AggregationRepository.F
118 public final class TimescaleTsKvEntity extends AbstractTsKvEntity implements ToData<TsKvEntry> { 126 public final class TimescaleTsKvEntity extends AbstractTsKvEntity implements ToData<TsKvEntry> {
119 127
120 @Id 128 @Id
121 - @Column(name = TENANT_ID_COLUMN)  
122 - private String tenantId; 129 + @Column(name = TENANT_ID_COLUMN, columnDefinition = "uuid")
  130 + private UUID tenantId;
123 131
124 @Id 132 @Id
125 - @Column(name = TS_COLUMN)  
126 - protected Long ts; 133 + @Column(name = ENTITY_ID_COLUMN, columnDefinition = "uuid")
  134 + protected UUID entityId;
127 135
128 - public TimescaleTsKvEntity() { } 136 + @Id
  137 + @Column(name = KEY_COLUMN)
  138 + protected int key;
  139 +
  140 + @Transient
  141 + protected String strKey;
  142 +
  143 +
  144 + public TimescaleTsKvEntity() {
  145 + }
129 146
130 public TimescaleTsKvEntity(Long tsBucket, Long interval, Long longValue, Double doubleValue, Long longCountValue, Long doubleCountValue, String strValue, String aggType) { 147 public TimescaleTsKvEntity(Long tsBucket, Long interval, Long longValue, Double doubleValue, Long longCountValue, Long doubleCountValue, String strValue, String aggType) {
131 if (!StringUtils.isEmpty(strValue)) { 148 if (!StringUtils.isEmpty(strValue)) {
132 this.strValue = strValue; 149 this.strValue = strValue;
133 } 150 }
134 if (!isAllNull(tsBucket, interval, longValue, doubleValue, longCountValue, doubleCountValue)) { 151 if (!isAllNull(tsBucket, interval, longValue, doubleValue, longCountValue, doubleCountValue)) {
135 - this.ts = tsBucket + interval/2; 152 + this.ts = tsBucket + interval / 2;
136 switch (aggType) { 153 switch (aggType) {
137 case AVG: 154 case AVG:
138 double sum = 0.0; 155 double sum = 0.0;
@@ -172,7 +189,7 @@ public final class TimescaleTsKvEntity extends AbstractTsKvEntity implements ToD @@ -172,7 +189,7 @@ public final class TimescaleTsKvEntity extends AbstractTsKvEntity implements ToD
172 189
173 public TimescaleTsKvEntity(Long tsBucket, Long interval, Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount) { 190 public TimescaleTsKvEntity(Long tsBucket, Long interval, Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount) {
174 if (!isAllNull(tsBucket, interval, booleanValueCount, strValueCount, longValueCount, doubleValueCount)) { 191 if (!isAllNull(tsBucket, interval, booleanValueCount, strValueCount, longValueCount, doubleValueCount)) {
175 - this.ts = tsBucket + interval/2; 192 + this.ts = tsBucket + interval / 2;
176 if (booleanValueCount != 0) { 193 if (booleanValueCount != 0) {
177 this.longValue = booleanValueCount; 194 this.longValue = booleanValueCount;
178 } else if (strValueCount != 0) { 195 } else if (strValueCount != 0) {
@@ -190,6 +207,17 @@ public final class TimescaleTsKvEntity extends AbstractTsKvEntity implements ToD @@ -190,6 +207,17 @@ public final class TimescaleTsKvEntity extends AbstractTsKvEntity implements ToD
190 207
191 @Override 208 @Override
192 public TsKvEntry toData() { 209 public TsKvEntry toData() {
193 - return new BasicTsKvEntry(ts, getKvEntry()); 210 + KvEntry kvEntry = null;
  211 + if (strValue != null) {
  212 + kvEntry = new StringDataEntry(strKey, strValue);
  213 + } else if (longValue != null) {
  214 + kvEntry = new LongDataEntry(strKey, longValue);
  215 + } else if (doubleValue != null) {
  216 + kvEntry = new DoubleDataEntry(strKey, doubleValue);
  217 + } else if (booleanValue != null) {
  218 + kvEntry = new BooleanDataEntry(strKey, booleanValue);
  219 + }
  220 + return new BasicTsKvEntry(ts, kvEntry);
194 } 221 }
  222 +
195 } 223 }
@@ -15,13 +15,8 @@ @@ -15,13 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.dao.sql; 16 package org.thingsboard.server.dao.sql;
17 17
18 -import com.google.common.util.concurrent.ListeningExecutorService;  
19 -import com.google.common.util.concurrent.MoreExecutors;  
20 import org.springframework.beans.factory.annotation.Autowired; 18 import org.springframework.beans.factory.annotation.Autowired;
21 19
22 -import javax.annotation.PreDestroy;  
23 -import java.util.concurrent.Executors;  
24 -  
25 public abstract class JpaAbstractDaoListeningExecutorService { 20 public abstract class JpaAbstractDaoListeningExecutorService {
26 21
27 @Autowired 22 @Autowired
@@ -21,8 +21,6 @@ import org.springframework.jdbc.core.JdbcTemplate; @@ -21,8 +21,6 @@ import org.springframework.jdbc.core.JdbcTemplate;
21 import org.springframework.stereotype.Repository; 21 import org.springframework.stereotype.Repository;
22 import org.springframework.transaction.support.TransactionTemplate; 22 import org.springframework.transaction.support.TransactionTemplate;
23 23
24 -import javax.persistence.EntityManager;  
25 -import javax.persistence.PersistenceContext;  
26 import java.util.regex.Pattern; 24 import java.util.regex.Pattern;
27 25
28 @Repository 26 @Repository
@@ -31,64 +29,15 @@ public abstract class AbstractInsertRepository { @@ -31,64 +29,15 @@ public abstract class AbstractInsertRepository {
31 private static final ThreadLocal<Pattern> PATTERN_THREAD_LOCAL = ThreadLocal.withInitial(() -> Pattern.compile(String.valueOf(Character.MIN_VALUE))); 29 private static final ThreadLocal<Pattern> PATTERN_THREAD_LOCAL = ThreadLocal.withInitial(() -> Pattern.compile(String.valueOf(Character.MIN_VALUE)));
32 private static final String EMPTY_STR = ""; 30 private static final String EMPTY_STR = "";
33 31
34 - protected static final String BOOL_V = "bool_v";  
35 - protected static final String STR_V = "str_v";  
36 - protected static final String LONG_V = "long_v";  
37 - protected static final String DBL_V = "dbl_v";  
38 -  
39 - protected static final String TS_KV_LATEST_TABLE = "ts_kv_latest";  
40 - protected static final String TS_KV_TABLE = "ts_kv";  
41 -  
42 - protected static final String HSQL_ON_BOOL_VALUE_UPDATE_SET_NULLS = getHsqlNullValues(TS_KV_TABLE, BOOL_V);  
43 - protected static final String HSQL_ON_STR_VALUE_UPDATE_SET_NULLS = getHsqlNullValues(TS_KV_TABLE, STR_V);  
44 - protected static final String HSQL_ON_LONG_VALUE_UPDATE_SET_NULLS = getHsqlNullValues(TS_KV_TABLE, LONG_V);  
45 - protected static final String HSQL_ON_DBL_VALUE_UPDATE_SET_NULLS = getHsqlNullValues(TS_KV_TABLE, DBL_V);  
46 -  
47 - protected static final String HSQL_LATEST_ON_BOOL_VALUE_UPDATE_SET_NULLS = getHsqlNullValues(TS_KV_LATEST_TABLE, BOOL_V);  
48 - protected static final String HSQL_LATEST_ON_STR_VALUE_UPDATE_SET_NULLS = getHsqlNullValues(TS_KV_LATEST_TABLE, STR_V);  
49 - protected static final String HSQL_LATEST_ON_LONG_VALUE_UPDATE_SET_NULLS = getHsqlNullValues(TS_KV_LATEST_TABLE, LONG_V);  
50 - protected static final String HSQL_LATEST_ON_DBL_VALUE_UPDATE_SET_NULLS = getHsqlNullValues(TS_KV_LATEST_TABLE, DBL_V);  
51 -  
52 - protected static final String PSQL_ON_BOOL_VALUE_UPDATE_SET_NULLS = "str_v = null, long_v = null, dbl_v = null";  
53 - protected static final String PSQL_ON_STR_VALUE_UPDATE_SET_NULLS = "bool_v = null, long_v = null, dbl_v = null";  
54 - protected static final String PSQL_ON_LONG_VALUE_UPDATE_SET_NULLS = "str_v = null, bool_v = null, dbl_v = null";  
55 - protected static final String PSQL_ON_DBL_VALUE_UPDATE_SET_NULLS = "str_v = null, long_v = null, bool_v = null";  
56 -  
57 @Value("${sql.remove_null_chars}") 32 @Value("${sql.remove_null_chars}")
58 private boolean removeNullChars; 33 private boolean removeNullChars;
59 34
60 - @PersistenceContext  
61 - protected EntityManager entityManager;  
62 -  
63 @Autowired 35 @Autowired
64 protected JdbcTemplate jdbcTemplate; 36 protected JdbcTemplate jdbcTemplate;
65 37
66 @Autowired 38 @Autowired
67 protected TransactionTemplate transactionTemplate; 39 protected TransactionTemplate transactionTemplate;
68 40
69 - protected static String getInsertOrUpdateStringHsql(String tableName, String constraint, String value, String nullValues) {  
70 - return "MERGE INTO " + tableName + " USING(VALUES :entity_type, :entity_id, :key, :ts, :" + value + ") A (entity_type, entity_id, key, ts, " + value + ") ON " + constraint + " WHEN MATCHED THEN UPDATE SET " + tableName + "." + value + " = A." + value + ", " + tableName + ".ts = A.ts," + nullValues + "WHEN NOT MATCHED THEN INSERT (entity_type, entity_id, key, ts, " + value + ") VALUES (A.entity_type, A.entity_id, A.key, A.ts, A." + value + ")";  
71 - }  
72 -  
73 - protected static String getInsertOrUpdateStringPsql(String tableName, String constraint, String value, String nullValues) {  
74 - return "INSERT INTO " + tableName + " (entity_type, entity_id, key, ts, " + value + ") VALUES (:entity_type, :entity_id, :key, :ts, :" + value + ") ON CONFLICT " + constraint + " DO UPDATE SET " + value + " = :" + value + ", ts = :ts," + nullValues;  
75 - }  
76 -  
77 - private static String getHsqlNullValues(String tableName, String notNullValue) {  
78 - switch (notNullValue) {  
79 - case BOOL_V:  
80 - return " " + tableName + ".str_v = null, " + tableName + ".long_v = null, " + tableName + ".dbl_v = null ";  
81 - case STR_V:  
82 - return " " + tableName + ".bool_v = null, " + tableName + ".long_v = null, " + tableName + ".dbl_v = null ";  
83 - case LONG_V:  
84 - return " " + tableName + ".str_v = null, " + tableName + ".bool_v = null, " + tableName + ".dbl_v = null ";  
85 - case DBL_V:  
86 - return " " + tableName + ".str_v = null, " + tableName + ".long_v = null, " + tableName + ".bool_v = null ";  
87 - default:  
88 - throw new RuntimeException("Unsupported insert value: [" + notNullValue + "]");  
89 - }  
90 - }  
91 -  
92 protected String replaceNullChars(String strValue) { 41 protected String replaceNullChars(String strValue) {
93 if (removeNullChars && strValue != null) { 42 if (removeNullChars && strValue != null) {
94 return PATTERN_THREAD_LOCAL.get().matcher(strValue).replaceAll(EMPTY_STR); 43 return PATTERN_THREAD_LOCAL.get().matcher(strValue).replaceAll(EMPTY_STR);
1 -/**  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.dao.sqlts;  
17 -  
18 -import org.springframework.data.jpa.repository.Modifying;  
19 -import org.springframework.stereotype.Repository;  
20 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvLatestEntity;  
21 -  
22 -import java.util.List;  
23 -  
24 -@Repository  
25 -public abstract class AbstractLatestInsertRepository extends AbstractInsertRepository {  
26 -  
27 - public abstract void saveOrUpdate(TsKvLatestEntity entity);  
28 -  
29 - public abstract void saveOrUpdate(List<TsKvLatestEntity> entities);  
30 -  
31 - protected void processSaveOrUpdate(TsKvLatestEntity entity, String requestBoolValue, String requestStrValue, String requestLongValue, String requestDblValue) {  
32 - if (entity.getBooleanValue() != null) {  
33 - saveOrUpdateBoolean(entity, requestBoolValue);  
34 - }  
35 - if (entity.getStrValue() != null) {  
36 - saveOrUpdateString(entity, requestStrValue);  
37 - }  
38 - if (entity.getLongValue() != null) {  
39 - saveOrUpdateLong(entity, requestLongValue);  
40 - }  
41 - if (entity.getDoubleValue() != null) {  
42 - saveOrUpdateDouble(entity, requestDblValue);  
43 - }  
44 - }  
45 -  
46 - @Modifying  
47 - protected abstract void saveOrUpdateBoolean(TsKvLatestEntity entity, String query);  
48 -  
49 - @Modifying  
50 - protected abstract void saveOrUpdateString(TsKvLatestEntity entity, String query);  
51 -  
52 - @Modifying  
53 - protected abstract void saveOrUpdateLong(TsKvLatestEntity entity, String query);  
54 -  
55 - @Modifying  
56 - protected abstract void saveOrUpdateDouble(TsKvLatestEntity entity, String query);  
57 -  
58 -}  
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts;
  17 +
  18 +import com.google.common.util.concurrent.Futures;
  19 +import com.google.common.util.concurrent.ListenableFuture;
  20 +import com.google.common.util.concurrent.SettableFuture;
  21 +import lombok.extern.slf4j.Slf4j;
  22 +import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.springframework.beans.factory.annotation.Value;
  24 +import org.thingsboard.server.common.data.id.EntityId;
  25 +import org.thingsboard.server.common.data.id.TenantId;
  26 +import org.thingsboard.server.common.data.kv.Aggregation;
  27 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
  28 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  29 +import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity;
  30 +import org.thingsboard.server.dao.sql.TbSqlBlockingQueue;
  31 +import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;
  32 +
  33 +import javax.annotation.PostConstruct;
  34 +import javax.annotation.PreDestroy;
  35 +import java.util.ArrayList;
  36 +import java.util.List;
  37 +import java.util.Optional;
  38 +import java.util.concurrent.CompletableFuture;
  39 +import java.util.stream.Collectors;
  40 +
  41 +@Slf4j
  42 +public abstract class AbstractSimpleSqlTimeseriesDao<T extends AbstractTsKvEntity> extends AbstractSqlTimeseriesDao {
  43 +
  44 + @Autowired
  45 + private InsertTsRepository<T> insertRepository;
  46 +
  47 + @Value("${sql.ts.batch_size:1000}")
  48 + private int tsBatchSize;
  49 +
  50 + @Value("${sql.ts.batch_max_delay:100}")
  51 + private long tsMaxDelay;
  52 +
  53 + @Value("${sql.ts.stats_print_interval_ms:1000}")
  54 + private long tsStatsPrintIntervalMs;
  55 +
  56 + protected TbSqlBlockingQueue<EntityContainer<T>> tsQueue;
  57 +
  58 + @PostConstruct
  59 + protected void init() {
  60 + super.init();
  61 + TbSqlBlockingQueueParams tsParams = TbSqlBlockingQueueParams.builder()
  62 + .logName("TS")
  63 + .batchSize(tsBatchSize)
  64 + .maxDelay(tsMaxDelay)
  65 + .statsPrintIntervalMs(tsStatsPrintIntervalMs)
  66 + .build();
  67 + tsQueue = new TbSqlBlockingQueue<>(tsParams);
  68 + tsQueue.init(logExecutor, v -> insertRepository.saveOrUpdate(v));
  69 + }
  70 +
  71 + @PreDestroy
  72 + protected void destroy() {
  73 + super.init();
  74 + if (tsQueue != null) {
  75 + tsQueue.destroy();
  76 + }
  77 + }
  78 +
  79 + protected ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
  80 + if (query.getAggregation() == Aggregation.NONE) {
  81 + return findAllAsyncWithLimit(entityId, query);
  82 + } else {
  83 + long stepTs = query.getStartTs();
  84 + List<ListenableFuture<Optional<TsKvEntry>>> futures = new ArrayList<>();
  85 + while (stepTs < query.getEndTs()) {
  86 + long startTs = stepTs;
  87 + long endTs = stepTs + query.getInterval();
  88 + long ts = startTs + (endTs - startTs) / 2;
  89 + futures.add(findAndAggregateAsync(entityId, query.getKey(), startTs, endTs, ts, query.getAggregation()));
  90 + stepTs = endTs;
  91 + }
  92 + return getTskvEntriesFuture(Futures.allAsList(futures));
  93 + }
  94 + }
  95 +
  96 + protected abstract ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(EntityId entityId, String key, long startTs, long endTs, long ts, Aggregation aggregation);
  97 +
  98 + protected abstract ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query);
  99 +
  100 + protected SettableFuture<T> setFutures(List<CompletableFuture<T>> entitiesFutures) {
  101 + SettableFuture<T> listenableFuture = SettableFuture.create();
  102 + CompletableFuture<List<T>> entities =
  103 + CompletableFuture.allOf(entitiesFutures.toArray(new CompletableFuture[entitiesFutures.size()]))
  104 + .thenApply(v -> entitiesFutures.stream()
  105 + .map(CompletableFuture::join)
  106 + .collect(Collectors.toList()));
  107 +
  108 + entities.whenComplete((tsKvEntities, throwable) -> {
  109 + if (throwable != null) {
  110 + listenableFuture.setException(throwable);
  111 + } else {
  112 + T result = null;
  113 + for (T entity : tsKvEntities) {
  114 + if (entity.isNotEmpty()) {
  115 + result = entity;
  116 + break;
  117 + }
  118 + }
  119 + listenableFuture.set(result);
  120 + }
  121 + });
  122 + return listenableFuture;
  123 + }
  124 +
  125 + protected void switchAgregation(EntityId entityId, String key, long startTs, long endTs, Aggregation aggregation, List<CompletableFuture<T>> entitiesFutures) {
  126 + switch (aggregation) {
  127 + case AVG:
  128 + findAvg(entityId, key, startTs, endTs, entitiesFutures);
  129 + break;
  130 + case MAX:
  131 + findMax(entityId, key, startTs, endTs, entitiesFutures);
  132 + break;
  133 + case MIN:
  134 + findMin(entityId, key, startTs, endTs, entitiesFutures);
  135 + break;
  136 + case SUM:
  137 + findSum(entityId, key, startTs, endTs, entitiesFutures);
  138 + break;
  139 + case COUNT:
  140 + findCount(entityId, key, startTs, endTs, entitiesFutures);
  141 + break;
  142 + default:
  143 + throw new IllegalArgumentException("Not supported aggregation type: " + aggregation);
  144 + }
  145 + }
  146 +
  147 + protected abstract void findCount(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<T>> entitiesFutures);
  148 +
  149 + protected abstract void findSum(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<T>> entitiesFutures);
  150 +
  151 + protected abstract void findMin(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<T>> entitiesFutures);
  152 +
  153 + protected abstract void findMax(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<T>> entitiesFutures);
  154 +
  155 + protected abstract void findAvg(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<T>> entitiesFutures);
  156 +}
@@ -16,20 +16,32 @@ @@ -16,20 +16,32 @@
16 package org.thingsboard.server.dao.sqlts; 16 package org.thingsboard.server.dao.sqlts;
17 17
18 import com.google.common.base.Function; 18 import com.google.common.base.Function;
  19 +import com.google.common.collect.Lists;
  20 +import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.Futures; 21 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture; 22 import com.google.common.util.concurrent.ListenableFuture;
21 -import com.google.common.util.concurrent.ListeningExecutorService;  
22 -import com.google.common.util.concurrent.MoreExecutors; 23 +import lombok.extern.slf4j.Slf4j;
  24 +import org.springframework.beans.factory.annotation.Autowired;
23 import org.springframework.beans.factory.annotation.Value; 25 import org.springframework.beans.factory.annotation.Value;
  26 +import org.thingsboard.server.common.data.UUIDConverter;
24 import org.thingsboard.server.common.data.id.EntityId; 27 import org.thingsboard.server.common.data.id.EntityId;
25 import org.thingsboard.server.common.data.id.TenantId; 28 import org.thingsboard.server.common.data.id.TenantId;
26 import org.thingsboard.server.common.data.kv.Aggregation; 29 import org.thingsboard.server.common.data.kv.Aggregation;
27 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; 30 import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
  31 +import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
28 import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; 32 import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
29 import org.thingsboard.server.common.data.kv.ReadTsKvQuery; 33 import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
  34 +import org.thingsboard.server.common.data.kv.StringDataEntry;
30 import org.thingsboard.server.common.data.kv.TsKvEntry; 35 import org.thingsboard.server.common.data.kv.TsKvEntry;
  36 +import org.thingsboard.server.dao.DaoUtil;
  37 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey;
  38 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
31 import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; 39 import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
32 -import org.thingsboard.server.dao.timeseries.TsInsertExecutorType; 40 +import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;
  41 +import org.thingsboard.server.dao.sql.TbSqlBlockingQueue;
  42 +import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;
  43 +import org.thingsboard.server.dao.sqlts.latest.TsKvLatestRepository;
  44 +import org.thingsboard.server.dao.timeseries.SimpleListenableFuture;
33 45
34 import javax.annotation.Nullable; 46 import javax.annotation.Nullable;
35 import javax.annotation.PostConstruct; 47 import javax.annotation.PostConstruct;
@@ -37,13 +49,55 @@ import javax.annotation.PreDestroy; @@ -37,13 +49,55 @@ import javax.annotation.PreDestroy;
37 import java.util.List; 49 import java.util.List;
38 import java.util.Objects; 50 import java.util.Objects;
39 import java.util.Optional; 51 import java.util.Optional;
40 -import java.util.concurrent.Executors; 52 +import java.util.concurrent.ExecutionException;
41 import java.util.stream.Collectors; 53 import java.util.stream.Collectors;
42 54
  55 +import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID;
  56 +
  57 +@Slf4j
43 public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningExecutorService { 58 public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningExecutorService {
44 59
45 private static final String DESC_ORDER = "DESC"; 60 private static final String DESC_ORDER = "DESC";
46 61
  62 + @Autowired
  63 + private TsKvLatestRepository tsKvLatestRepository;
  64 +
  65 + @Autowired
  66 + private InsertLatestRepository insertLatestRepository;
  67 +
  68 + @Autowired
  69 + protected ScheduledLogExecutorComponent logExecutor;
  70 +
  71 + @Value("${sql.ts_latest.batch_size:1000}")
  72 + private int tsLatestBatchSize;
  73 +
  74 + @Value("${sql.ts_latest.batch_max_delay:100}")
  75 + private long tsLatestMaxDelay;
  76 +
  77 + @Value("${sql.ts_latest.stats_print_interval_ms:1000}")
  78 + private long tsLatestStatsPrintIntervalMs;
  79 +
  80 + private TbSqlBlockingQueue<TsKvLatestEntity> tsLatestQueue;
  81 +
  82 + @PostConstruct
  83 + protected void init() {
  84 + TbSqlBlockingQueueParams tsLatestParams = TbSqlBlockingQueueParams.builder()
  85 + .logName("TS Latest")
  86 + .batchSize(tsLatestBatchSize)
  87 + .maxDelay(tsLatestMaxDelay)
  88 + .statsPrintIntervalMs(tsLatestStatsPrintIntervalMs)
  89 + .build();
  90 + tsLatestQueue = new TbSqlBlockingQueue<>(tsLatestParams);
  91 + tsLatestQueue.init(logExecutor, v -> insertLatestRepository.saveOrUpdate(v));
  92 + }
  93 +
  94 + @PreDestroy
  95 + protected void destroy() {
  96 + if (tsLatestQueue != null) {
  97 + tsLatestQueue.destroy();
  98 + }
  99 + }
  100 +
47 protected ListenableFuture<List<TsKvEntry>> processFindAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) { 101 protected ListenableFuture<List<TsKvEntry>> processFindAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
48 List<ListenableFuture<List<TsKvEntry>>> futures = queries 102 List<ListenableFuture<List<TsKvEntry>>> futures = queries
49 .stream() 103 .stream()
@@ -89,4 +143,105 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx @@ -89,4 +143,105 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx
89 Aggregation.NONE, DESC_ORDER); 143 Aggregation.NONE, DESC_ORDER);
90 return findAllAsync(tenantId, entityId, findNewLatestQuery); 144 return findAllAsync(tenantId, entityId, findNewLatestQuery);
91 } 145 }
  146 +
  147 + protected ListenableFuture<TsKvEntry> getFindLatestFuture(EntityId entityId, String key) {
  148 + TsKvLatestCompositeKey compositeKey =
  149 + new TsKvLatestCompositeKey(
  150 + entityId.getEntityType(),
  151 + fromTimeUUID(entityId.getId()),
  152 + key);
  153 + Optional<TsKvLatestEntity> entry = tsKvLatestRepository.findById(compositeKey);
  154 + TsKvEntry result;
  155 + if (entry.isPresent()) {
  156 + result = DaoUtil.getData(entry.get());
  157 + } else {
  158 + result = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));
  159 + }
  160 + return Futures.immediateFuture(result);
  161 + }
  162 +
  163 + protected ListenableFuture<Void> getRemoveLatestFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  164 + ListenableFuture<TsKvEntry> latestFuture = getFindLatestFuture(entityId, query.getKey());
  165 +
  166 + ListenableFuture<Boolean> booleanFuture = Futures.transform(latestFuture, tsKvEntry -> {
  167 + long ts = tsKvEntry.getTs();
  168 + return ts > query.getStartTs() && ts <= query.getEndTs();
  169 + }, service);
  170 +
  171 + ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  172 + if (isRemove) {
  173 + TsKvLatestEntity latestEntity = new TsKvLatestEntity();
  174 + latestEntity.setEntityType(entityId.getEntityType());
  175 + latestEntity.setEntityId(fromTimeUUID(entityId.getId()));
  176 + latestEntity.setKey(query.getKey());
  177 + return service.submit(() -> {
  178 + tsKvLatestRepository.delete(latestEntity);
  179 + return null;
  180 + });
  181 + }
  182 + return Futures.immediateFuture(null);
  183 + }, service);
  184 +
  185 + final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();
  186 + Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {
  187 + @Override
  188 + public void onSuccess(@Nullable Void result) {
  189 + if (query.getRewriteLatestIfDeleted()) {
  190 + ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {
  191 + if (isRemove) {
  192 + return getNewLatestEntryFuture(tenantId, entityId, query);
  193 + }
  194 + return Futures.immediateFuture(null);
  195 + }, service);
  196 +
  197 + try {
  198 + resultFuture.set(savedLatestFuture.get());
  199 + } catch (InterruptedException | ExecutionException e) {
  200 + log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);
  201 + }
  202 + } else {
  203 + resultFuture.set(null);
  204 + }
  205 + }
  206 +
  207 + @Override
  208 + public void onFailure(Throwable t) {
  209 + log.warn("[{}] Failed to process remove of the latest value", entityId, t);
  210 + }
  211 + });
  212 + return resultFuture;
  213 + }
  214 +
  215 + protected ListenableFuture<List<TsKvEntry>> getFindAllLatestFuture(EntityId entityId) {
  216 + return Futures.immediateFuture(
  217 + DaoUtil.convertDataList(Lists.newArrayList(
  218 + tsKvLatestRepository.findAllByEntityTypeAndEntityId(
  219 + entityId.getEntityType(),
  220 + UUIDConverter.fromTimeUUID(entityId.getId())))));
  221 + }
  222 +
  223 + protected ListenableFuture<Void> getSaveLatestFuture(EntityId entityId, TsKvEntry tsKvEntry) {
  224 + TsKvLatestEntity latestEntity = new TsKvLatestEntity();
  225 + latestEntity.setEntityType(entityId.getEntityType());
  226 + latestEntity.setEntityId(fromTimeUUID(entityId.getId()));
  227 + latestEntity.setTs(tsKvEntry.getTs());
  228 + latestEntity.setKey(tsKvEntry.getKey());
  229 + latestEntity.setStrValue(tsKvEntry.getStrValue().orElse(null));
  230 + latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
  231 + latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null));
  232 + latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
  233 + return tsLatestQueue.add(latestEntity);
  234 + }
  235 +
  236 + private ListenableFuture<Void> getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  237 + ListenableFuture<List<TsKvEntry>> future = findNewLatestEntryFuture(tenantId, entityId, query);
  238 + return Futures.transformAsync(future, entryList -> {
  239 + if (entryList.size() == 1) {
  240 + return getSaveLatestFuture(entityId, entryList.get(0));
  241 + } else {
  242 + log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());
  243 + }
  244 + return Futures.immediateFuture(null);
  245 + }, service);
  246 + }
92 } 247 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity;
  21 +
  22 +@Data
  23 +@AllArgsConstructor
  24 +public class EntityContainer<T extends AbstractTsKvEntity> {
  25 +
  26 + private T entity;
  27 + private String partitionDate;
  28 +
  29 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts;
  17 +
  18 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
  19 +
  20 +import java.util.List;
  21 +
  22 +public interface InsertLatestRepository {
  23 +
  24 + void saveOrUpdate(List<TsKvLatestEntity> entities);
  25 +
  26 +}
dao/src/main/java/org/thingsboard/server/dao/sqlts/InsertTsRepository.java renamed from dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractTimeseriesInsertRepository.java
@@ -15,44 +15,12 @@ @@ -15,44 +15,12 @@
15 */ 15 */
16 package org.thingsboard.server.dao.sqlts; 16 package org.thingsboard.server.dao.sqlts;
17 17
18 -import org.springframework.data.jpa.repository.Modifying;  
19 -import org.springframework.stereotype.Repository;  
20 import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; 18 import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity;
21 19
22 import java.util.List; 20 import java.util.List;
23 21
24 -@Repository  
25 -public abstract class AbstractTimeseriesInsertRepository<T extends AbstractTsKvEntity> extends AbstractInsertRepository { 22 +public interface InsertTsRepository<T extends AbstractTsKvEntity> {
26 23
27 - public abstract void saveOrUpdate(T entity); 24 + void saveOrUpdate(List<EntityContainer<T>> entities);
28 25
29 - public abstract void saveOrUpdate(List<T> entities);  
30 -  
31 - protected void processSaveOrUpdate(T entity, String requestBoolValue, String requestStrValue, String requestLongValue, String requestDblValue) {  
32 - if (entity.getBooleanValue() != null) {  
33 - saveOrUpdateBoolean(entity, requestBoolValue);  
34 - }  
35 - if (entity.getStrValue() != null) {  
36 - saveOrUpdateString(entity, requestStrValue);  
37 - }  
38 - if (entity.getLongValue() != null) {  
39 - saveOrUpdateLong(entity, requestLongValue);  
40 - }  
41 - if (entity.getDoubleValue() != null) {  
42 - saveOrUpdateDouble(entity, requestDblValue);  
43 - }  
44 - }  
45 -  
46 - @Modifying  
47 - protected abstract void saveOrUpdateBoolean(T entity, String query);  
48 -  
49 - @Modifying  
50 - protected abstract void saveOrUpdateString(T entity, String query);  
51 -  
52 - @Modifying  
53 - protected abstract void saveOrUpdateLong(T entity, String query);  
54 -  
55 - @Modifying  
56 - protected abstract void saveOrUpdateDouble(T entity, String query);  
57 -  
58 -}  
  26 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts.dictionary;
  17 +
  18 +import org.springframework.data.repository.CrudRepository;
  19 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;
  20 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey;
  21 +import org.thingsboard.server.dao.util.PsqlDao;
  22 +
  23 +import java.util.Optional;
  24 +
  25 +@PsqlDao
  26 +public interface TsKvDictionaryRepository extends CrudRepository<TsKvDictionary, TsKvDictionaryCompositeKey> {
  27 +
  28 + Optional<TsKvDictionary> findByKeyId(int keyId);
  29 +
  30 +}
dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/HsqlTimeseriesInsertRepository.java renamed from dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/HsqlTimeseriesInsertRepository.java
@@ -13,13 +13,15 @@ @@ -13,13 +13,15 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.dao.sqlts.ts; 16 +package org.thingsboard.server.dao.sqlts.hsql;
17 17
18 import org.springframework.jdbc.core.BatchPreparedStatementSetter; 18 import org.springframework.jdbc.core.BatchPreparedStatementSetter;
19 import org.springframework.stereotype.Repository; 19 import org.springframework.stereotype.Repository;
20 import org.springframework.transaction.annotation.Transactional; 20 import org.springframework.transaction.annotation.Transactional;
21 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity;  
22 -import org.thingsboard.server.dao.sqlts.AbstractTimeseriesInsertRepository; 21 +import org.thingsboard.server.dao.model.sqlts.hsql.TsKvEntity;
  22 +import org.thingsboard.server.dao.sqlts.AbstractInsertRepository;
  23 +import org.thingsboard.server.dao.sqlts.EntityContainer;
  24 +import org.thingsboard.server.dao.sqlts.InsertTsRepository;
23 import org.thingsboard.server.dao.util.HsqlDao; 25 import org.thingsboard.server.dao.util.HsqlDao;
24 import org.thingsboard.server.dao.util.SqlTsDao; 26 import org.thingsboard.server.dao.util.SqlTsDao;
25 27
@@ -32,14 +34,7 @@ import java.util.List; @@ -32,14 +34,7 @@ import java.util.List;
32 @HsqlDao 34 @HsqlDao
33 @Repository 35 @Repository
34 @Transactional 36 @Transactional
35 -public class HsqlTimeseriesInsertRepository extends AbstractTimeseriesInsertRepository<TsKvEntity> {  
36 -  
37 - private static final String TS_KV_CONSTRAINT = "(ts_kv.entity_type=A.entity_type AND ts_kv.entity_id=A.entity_id AND ts_kv.key=A.key AND ts_kv.ts=A.ts)";  
38 -  
39 - private static final String INSERT_OR_UPDATE_BOOL_STATEMENT = getInsertOrUpdateStringHsql(TS_KV_TABLE, TS_KV_CONSTRAINT, BOOL_V, HSQL_ON_BOOL_VALUE_UPDATE_SET_NULLS);  
40 - private static final String INSERT_OR_UPDATE_STR_STATEMENT = getInsertOrUpdateStringHsql(TS_KV_TABLE, TS_KV_CONSTRAINT, STR_V, HSQL_ON_STR_VALUE_UPDATE_SET_NULLS);  
41 - private static final String INSERT_OR_UPDATE_LONG_STATEMENT = getInsertOrUpdateStringHsql(TS_KV_TABLE, TS_KV_CONSTRAINT, LONG_V, HSQL_ON_LONG_VALUE_UPDATE_SET_NULLS);  
42 - private static final String INSERT_OR_UPDATE_DBL_STATEMENT = getInsertOrUpdateStringHsql(TS_KV_TABLE, TS_KV_CONSTRAINT, DBL_V, HSQL_ON_DBL_VALUE_UPDATE_SET_NULLS); 37 +public class HsqlTimeseriesInsertRepository extends AbstractInsertRepository implements InsertTsRepository<TsKvEntity> {
43 38
44 private static final String INSERT_OR_UPDATE = 39 private static final String INSERT_OR_UPDATE =
45 "MERGE INTO ts_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?) " + 40 "MERGE INTO ts_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?) " +
@@ -53,36 +48,33 @@ public class HsqlTimeseriesInsertRepository extends AbstractTimeseriesInsertRepo @@ -53,36 +48,33 @@ public class HsqlTimeseriesInsertRepository extends AbstractTimeseriesInsertRepo
53 "VALUES (T.entity_type, T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v);"; 48 "VALUES (T.entity_type, T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v);";
54 49
55 @Override 50 @Override
56 - public void saveOrUpdate(TsKvEntity entity) {  
57 - processSaveOrUpdate(entity, INSERT_OR_UPDATE_BOOL_STATEMENT, INSERT_OR_UPDATE_STR_STATEMENT, INSERT_OR_UPDATE_LONG_STATEMENT, INSERT_OR_UPDATE_DBL_STATEMENT);  
58 - }  
59 -  
60 - @Override  
61 - public void saveOrUpdate(List<TsKvEntity> entities) { 51 + public void saveOrUpdate(List<EntityContainer<TsKvEntity>> entities) {
62 jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() { 52 jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() {
63 @Override 53 @Override
64 public void setValues(PreparedStatement ps, int i) throws SQLException { 54 public void setValues(PreparedStatement ps, int i) throws SQLException {
65 - ps.setString(1, entities.get(i).getEntityType().name());  
66 - ps.setString(2, entities.get(i).getEntityId());  
67 - ps.setString(3, entities.get(i).getKey());  
68 - ps.setLong(4, entities.get(i).getTs());  
69 -  
70 - if (entities.get(i).getBooleanValue() != null) {  
71 - ps.setBoolean(5, entities.get(i).getBooleanValue()); 55 + EntityContainer<TsKvEntity> tsKvEntityEntityContainer = entities.get(i);
  56 + TsKvEntity tsKvEntity = tsKvEntityEntityContainer.getEntity();
  57 + ps.setString(1, tsKvEntity.getEntityType().name());
  58 + ps.setString(2, tsKvEntity.getEntityId());
  59 + ps.setString(3, tsKvEntity.getKey());
  60 + ps.setLong(4, tsKvEntity.getTs());
  61 +
  62 + if (tsKvEntity.getBooleanValue() != null) {
  63 + ps.setBoolean(5, tsKvEntity.getBooleanValue());
72 } else { 64 } else {
73 ps.setNull(5, Types.BOOLEAN); 65 ps.setNull(5, Types.BOOLEAN);
74 } 66 }
75 67
76 - ps.setString(6, entities.get(i).getStrValue()); 68 + ps.setString(6, tsKvEntity.getStrValue());
77 69
78 - if (entities.get(i).getLongValue() != null) {  
79 - ps.setLong(7, entities.get(i).getLongValue()); 70 + if (tsKvEntity.getLongValue() != null) {
  71 + ps.setLong(7, tsKvEntity.getLongValue());
80 } else { 72 } else {
81 ps.setNull(7, Types.BIGINT); 73 ps.setNull(7, Types.BIGINT);
82 } 74 }
83 75
84 - if (entities.get(i).getDoubleValue() != null) {  
85 - ps.setDouble(8, entities.get(i).getDoubleValue()); 76 + if (tsKvEntity.getDoubleValue() != null) {
  77 + ps.setDouble(8, tsKvEntity.getDoubleValue());
86 } else { 78 } else {
87 ps.setNull(8, Types.DOUBLE); 79 ps.setNull(8, Types.DOUBLE);
88 } 80 }
@@ -94,48 +86,4 @@ public class HsqlTimeseriesInsertRepository extends AbstractTimeseriesInsertRepo @@ -94,48 +86,4 @@ public class HsqlTimeseriesInsertRepository extends AbstractTimeseriesInsertRepo
94 } 86 }
95 }); 87 });
96 } 88 }
97 -  
98 - @Override  
99 - protected void saveOrUpdateBoolean(TsKvEntity entity, String query) {  
100 - entityManager.createNativeQuery(query)  
101 - .setParameter("entity_type", entity.getEntityType().name())  
102 - .setParameter("entity_id", entity.getEntityId())  
103 - .setParameter("key", entity.getKey())  
104 - .setParameter("ts", entity.getTs())  
105 - .setParameter("bool_v", entity.getBooleanValue())  
106 - .executeUpdate();  
107 - }  
108 -  
109 - @Override  
110 - protected void saveOrUpdateString(TsKvEntity entity, String query) {  
111 - entityManager.createNativeQuery(query)  
112 - .setParameter("entity_type", entity.getEntityType().name())  
113 - .setParameter("entity_id", entity.getEntityId())  
114 - .setParameter("key", entity.getKey())  
115 - .setParameter("ts", entity.getTs())  
116 - .setParameter("str_v", entity.getStrValue())  
117 - .executeUpdate();  
118 - }  
119 -  
120 - @Override  
121 - protected void saveOrUpdateLong(TsKvEntity entity, String query) {  
122 - entityManager.createNativeQuery(query)  
123 - .setParameter("entity_type", entity.getEntityType().name())  
124 - .setParameter("entity_id", entity.getEntityId())  
125 - .setParameter("key", entity.getKey())  
126 - .setParameter("ts", entity.getTs())  
127 - .setParameter("long_v", entity.getLongValue())  
128 - .executeUpdate();  
129 - }  
130 -  
131 - @Override  
132 - protected void saveOrUpdateDouble(TsKvEntity entity, String query) {  
133 - entityManager.createNativeQuery(query)  
134 - .setParameter("entity_type", entity.getEntityType().name())  
135 - .setParameter("entity_id", entity.getEntityId())  
136 - .setParameter("key", entity.getKey())  
137 - .setParameter("ts", entity.getTs())  
138 - .setParameter("dbl_v", entity.getDoubleValue())  
139 - .executeUpdate();  
140 - }  
141 } 89 }
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts.hsql;
  17 +
  18 +import com.google.common.util.concurrent.Futures;
  19 +import com.google.common.util.concurrent.ListenableFuture;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.data.domain.PageRequest;
  23 +import org.springframework.data.domain.Sort;
  24 +import org.springframework.stereotype.Component;
  25 +import org.thingsboard.server.common.data.id.EntityId;
  26 +import org.thingsboard.server.common.data.id.TenantId;
  27 +import org.thingsboard.server.common.data.kv.Aggregation;
  28 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  29 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
  30 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  31 +import org.thingsboard.server.dao.DaoUtil;
  32 +import org.thingsboard.server.dao.model.sqlts.hsql.TsKvEntity;
  33 +import org.thingsboard.server.dao.sqlts.AbstractSimpleSqlTimeseriesDao;
  34 +import org.thingsboard.server.dao.sqlts.EntityContainer;
  35 +import org.thingsboard.server.dao.timeseries.TimeseriesDao;
  36 +import org.thingsboard.server.dao.util.HsqlDao;
  37 +import org.thingsboard.server.dao.util.SqlTsDao;
  38 +
  39 +import java.util.ArrayList;
  40 +import java.util.List;
  41 +import java.util.Optional;
  42 +import java.util.concurrent.CompletableFuture;
  43 +
  44 +import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID;
  45 +
  46 +
  47 +@Component
  48 +@Slf4j
  49 +@SqlTsDao
  50 +@HsqlDao
  51 +public class JpaHsqlTimeseriesDao extends AbstractSimpleSqlTimeseriesDao<TsKvEntity> implements TimeseriesDao {
  52 +
  53 + @Autowired
  54 + private TsKvHsqlRepository tsKvRepository;
  55 +
  56 + @Override
  57 + public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
  58 + return processFindAllAsync(tenantId, entityId, queries);
  59 + }
  60 +
  61 + @Override
  62 + public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
  63 + TsKvEntity entity = new TsKvEntity();
  64 + entity.setEntityType(entityId.getEntityType());
  65 + entity.setEntityId(fromTimeUUID(entityId.getId()));
  66 + entity.setTs(tsKvEntry.getTs());
  67 + entity.setKey(tsKvEntry.getKey());
  68 + entity.setStrValue(tsKvEntry.getStrValue().orElse(null));
  69 + entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
  70 + entity.setLongValue(tsKvEntry.getLongValue().orElse(null));
  71 + entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
  72 + log.trace("Saving entity: {}", entity);
  73 + return tsQueue.add(new EntityContainer(entity, null));
  74 + }
  75 +
  76 + @Override
  77 + public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  78 + return service.submit(() -> {
  79 + tsKvRepository.delete(
  80 + fromTimeUUID(entityId.getId()),
  81 + entityId.getEntityType(),
  82 + query.getKey(),
  83 + query.getStartTs(),
  84 + query.getEndTs());
  85 + return null;
  86 + });
  87 + }
  88 +
  89 + @Override
  90 + public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
  91 + return getSaveLatestFuture(entityId, tsKvEntry);
  92 + }
  93 +
  94 + @Override
  95 + public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  96 + return getRemoveLatestFuture(tenantId, entityId, query);
  97 + }
  98 +
  99 + @Override
  100 + public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
  101 + return getFindLatestFuture(entityId, key);
  102 + }
  103 +
  104 + @Override
  105 + public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
  106 + return getFindAllLatestFuture(entityId);
  107 + }
  108 +
  109 + @Override
  110 + public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
  111 + return Futures.immediateFuture(null);
  112 + }
  113 +
  114 + @Override
  115 + public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  116 + return Futures.immediateFuture(null);
  117 + }
  118 +
  119 + protected ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(EntityId entityId, String key, long startTs, long endTs, long ts, Aggregation aggregation) {
  120 + List<CompletableFuture<TsKvEntity>> entitiesFutures = new ArrayList<>();
  121 + switchAgregation(entityId, key, startTs, endTs, aggregation, entitiesFutures);
  122 + return Futures.transform(setFutures(entitiesFutures), entity -> {
  123 + if (entity != null && entity.isNotEmpty()) {
  124 + entity.setEntityId(fromTimeUUID(entityId.getId()));
  125 + entity.setEntityType(entityId.getEntityType());
  126 + entity.setKey(key);
  127 + entity.setTs(ts);
  128 + return Optional.of(DaoUtil.getData(entity));
  129 + } else {
  130 + return Optional.empty();
  131 + }
  132 + });
  133 + }
  134 +
  135 + protected ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
  136 + return Futures.immediateFuture(
  137 + DaoUtil.convertDataList(
  138 + tsKvRepository.findAllWithLimit(
  139 + fromTimeUUID(entityId.getId()),
  140 + entityId.getEntityType(),
  141 + query.getKey(),
  142 + query.getStartTs(),
  143 + query.getEndTs(),
  144 + new PageRequest(0, query.getLimit(),
  145 + new Sort(Sort.Direction.fromString(
  146 + query.getOrderBy()), "ts")))));
  147 + }
  148 +
  149 + protected void findCount(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures) {
  150 + entitiesFutures.add(tsKvRepository.findCount(
  151 + fromTimeUUID(entityId.getId()),
  152 + entityId.getEntityType(),
  153 + key,
  154 + startTs,
  155 + endTs));
  156 + }
  157 +
  158 + protected void findSum(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures) {
  159 + entitiesFutures.add(tsKvRepository.findSum(
  160 + fromTimeUUID(entityId.getId()),
  161 + entityId.getEntityType(),
  162 + key,
  163 + startTs,
  164 + endTs));
  165 + }
  166 +
  167 + protected void findMin(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures) {
  168 + entitiesFutures.add(tsKvRepository.findStringMin(
  169 + fromTimeUUID(entityId.getId()),
  170 + entityId.getEntityType(),
  171 + key,
  172 + startTs,
  173 + endTs));
  174 + entitiesFutures.add(tsKvRepository.findNumericMin(
  175 + fromTimeUUID(entityId.getId()),
  176 + entityId.getEntityType(),
  177 + key,
  178 + startTs,
  179 + endTs));
  180 + }
  181 +
  182 + protected void findMax(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures) {
  183 + entitiesFutures.add(tsKvRepository.findStringMax(
  184 + fromTimeUUID(entityId.getId()),
  185 + entityId.getEntityType(),
  186 + key,
  187 + startTs,
  188 + endTs));
  189 + entitiesFutures.add(tsKvRepository.findNumericMax(
  190 + fromTimeUUID(entityId.getId()),
  191 + entityId.getEntityType(),
  192 + key,
  193 + startTs,
  194 + endTs));
  195 + }
  196 +
  197 + protected void findAvg(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures) {
  198 + entitiesFutures.add(tsKvRepository.findAvg(
  199 + fromTimeUUID(entityId.getId()),
  200 + entityId.getEntityType(),
  201 + key,
  202 + startTs,
  203 + endTs));
  204 + }
  205 +
  206 +}
dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/TsKvHsqlRepository.java renamed from dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/TsKvRepository.java
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.dao.sqlts.ts; 16 +package org.thingsboard.server.dao.sqlts.hsql;
17 17
18 import org.springframework.data.domain.Pageable; 18 import org.springframework.data.domain.Pageable;
19 import org.springframework.data.jpa.repository.Modifying; 19 import org.springframework.data.jpa.repository.Modifying;
@@ -23,15 +23,15 @@ import org.springframework.data.repository.query.Param; @@ -23,15 +23,15 @@ import org.springframework.data.repository.query.Param;
23 import org.springframework.scheduling.annotation.Async; 23 import org.springframework.scheduling.annotation.Async;
24 import org.springframework.transaction.annotation.Transactional; 24 import org.springframework.transaction.annotation.Transactional;
25 import org.thingsboard.server.common.data.EntityType; 25 import org.thingsboard.server.common.data.EntityType;
26 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvCompositeKey;  
27 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; 26 +import org.thingsboard.server.dao.model.sqlts.hsql.TsKvCompositeKey;
  27 +import org.thingsboard.server.dao.model.sqlts.hsql.TsKvEntity;
28 import org.thingsboard.server.dao.util.SqlDao; 28 import org.thingsboard.server.dao.util.SqlDao;
29 29
30 import java.util.List; 30 import java.util.List;
31 import java.util.concurrent.CompletableFuture; 31 import java.util.concurrent.CompletableFuture;
32 32
33 @SqlDao 33 @SqlDao
34 -public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvCompositeKey> { 34 +public interface TsKvHsqlRepository extends CrudRepository<TsKvEntity, TsKvCompositeKey> {
35 35
36 @Query("SELECT tskv FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " + 36 @Query("SELECT tskv FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " +
37 "AND tskv.entityType = :entityType AND tskv.key = :entityKey " + 37 "AND tskv.entityType = :entityType AND tskv.key = :entityKey " +
@@ -146,4 +146,4 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite @@ -146,4 +146,4 @@ public interface TsKvRepository extends CrudRepository<TsKvEntity, TsKvComposite
146 @Param("startTs") long startTs, 146 @Param("startTs") long startTs,
147 @Param("endTs") long endTs); 147 @Param("endTs") long endTs);
148 148
149 -} 149 +}
dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/HsqlLatestInsertRepository.java renamed from dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/HsqlLatestInsertRepository.java
@@ -13,13 +13,14 @@ @@ -13,13 +13,14 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.dao.sqlts.ts; 16 +package org.thingsboard.server.dao.sqlts.latest;
17 17
18 import org.springframework.jdbc.core.BatchPreparedStatementSetter; 18 import org.springframework.jdbc.core.BatchPreparedStatementSetter;
19 import org.springframework.stereotype.Repository; 19 import org.springframework.stereotype.Repository;
20 import org.springframework.transaction.annotation.Transactional; 20 import org.springframework.transaction.annotation.Transactional;
21 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvLatestEntity;  
22 -import org.thingsboard.server.dao.sqlts.AbstractLatestInsertRepository; 21 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
  22 +import org.thingsboard.server.dao.sqlts.AbstractInsertRepository;
  23 +import org.thingsboard.server.dao.sqlts.InsertLatestRepository;
23 import org.thingsboard.server.dao.util.HsqlDao; 24 import org.thingsboard.server.dao.util.HsqlDao;
24 import org.thingsboard.server.dao.util.SqlTsDao; 25 import org.thingsboard.server.dao.util.SqlTsDao;
25 26
@@ -32,14 +33,7 @@ import java.util.List; @@ -32,14 +33,7 @@ import java.util.List;
32 @HsqlDao 33 @HsqlDao
33 @Repository 34 @Repository
34 @Transactional 35 @Transactional
35 -public class HsqlLatestInsertRepository extends AbstractLatestInsertRepository {  
36 -  
37 - private static final String TS_KV_LATEST_CONSTRAINT = "(ts_kv_latest.entity_type=A.entity_type AND ts_kv_latest.entity_id=A.entity_id AND ts_kv_latest.key=A.key)";  
38 -  
39 - private static final String INSERT_OR_UPDATE_BOOL_STATEMENT = getInsertOrUpdateStringHsql(TS_KV_LATEST_TABLE, TS_KV_LATEST_CONSTRAINT, BOOL_V, HSQL_LATEST_ON_BOOL_VALUE_UPDATE_SET_NULLS);  
40 - private static final String INSERT_OR_UPDATE_STR_STATEMENT = getInsertOrUpdateStringHsql(TS_KV_LATEST_TABLE, TS_KV_LATEST_CONSTRAINT, STR_V, HSQL_LATEST_ON_STR_VALUE_UPDATE_SET_NULLS);  
41 - private static final String INSERT_OR_UPDATE_LONG_STATEMENT = getInsertOrUpdateStringHsql(TS_KV_LATEST_TABLE, TS_KV_LATEST_CONSTRAINT, LONG_V, HSQL_LATEST_ON_LONG_VALUE_UPDATE_SET_NULLS);  
42 - private static final String INSERT_OR_UPDATE_DBL_STATEMENT = getInsertOrUpdateStringHsql(TS_KV_LATEST_TABLE, TS_KV_LATEST_CONSTRAINT, DBL_V, HSQL_LATEST_ON_DBL_VALUE_UPDATE_SET_NULLS); 36 +public class HsqlLatestInsertRepository extends AbstractInsertRepository implements InsertLatestRepository {
43 37
44 private static final String INSERT_OR_UPDATE = 38 private static final String INSERT_OR_UPDATE =
45 "MERGE INTO ts_kv_latest USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?) " + 39 "MERGE INTO ts_kv_latest USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?) " +
@@ -52,11 +46,6 @@ public class HsqlLatestInsertRepository extends AbstractLatestInsertRepository { @@ -52,11 +46,6 @@ public class HsqlLatestInsertRepository extends AbstractLatestInsertRepository {
52 "VALUES (T.entity_type, T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v);"; 46 "VALUES (T.entity_type, T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v);";
53 47
54 @Override 48 @Override
55 - public void saveOrUpdate(TsKvLatestEntity entity) {  
56 - processSaveOrUpdate(entity, INSERT_OR_UPDATE_BOOL_STATEMENT, INSERT_OR_UPDATE_STR_STATEMENT, INSERT_OR_UPDATE_LONG_STATEMENT, INSERT_OR_UPDATE_DBL_STATEMENT);  
57 - }  
58 -  
59 - @Override  
60 public void saveOrUpdate(List<TsKvLatestEntity> entities) { 49 public void saveOrUpdate(List<TsKvLatestEntity> entities) {
61 jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() { 50 jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() {
62 @Override 51 @Override
@@ -93,48 +82,4 @@ public class HsqlLatestInsertRepository extends AbstractLatestInsertRepository { @@ -93,48 +82,4 @@ public class HsqlLatestInsertRepository extends AbstractLatestInsertRepository {
93 } 82 }
94 }); 83 });
95 } 84 }
96 -  
97 - @Override  
98 - protected void saveOrUpdateBoolean(TsKvLatestEntity entity, String query) {  
99 - entityManager.createNativeQuery(query)  
100 - .setParameter("entity_type", entity.getEntityType().name())  
101 - .setParameter("entity_id", entity.getEntityId())  
102 - .setParameter("key", entity.getKey())  
103 - .setParameter("ts", entity.getTs())  
104 - .setParameter("bool_v", entity.getBooleanValue())  
105 - .executeUpdate();  
106 - }  
107 -  
108 - @Override  
109 - protected void saveOrUpdateString(TsKvLatestEntity entity, String query) {  
110 - entityManager.createNativeQuery(query)  
111 - .setParameter("entity_type", entity.getEntityType().name())  
112 - .setParameter("entity_id", entity.getEntityId())  
113 - .setParameter("key", entity.getKey())  
114 - .setParameter("ts", entity.getTs())  
115 - .setParameter("str_v", entity.getStrValue())  
116 - .executeUpdate();  
117 - }  
118 -  
119 - @Override  
120 - protected void saveOrUpdateLong(TsKvLatestEntity entity, String query) {  
121 - entityManager.createNativeQuery(query)  
122 - .setParameter("entity_type", entity.getEntityType().name())  
123 - .setParameter("entity_id", entity.getEntityId())  
124 - .setParameter("key", entity.getKey())  
125 - .setParameter("ts", entity.getTs())  
126 - .setParameter("long_v", entity.getLongValue())  
127 - .executeUpdate();  
128 - }  
129 -  
130 - @Override  
131 - protected void saveOrUpdateDouble(TsKvLatestEntity entity, String query) {  
132 - entityManager.createNativeQuery(query)  
133 - .setParameter("entity_type", entity.getEntityType().name())  
134 - .setParameter("entity_id", entity.getEntityId())  
135 - .setParameter("key", entity.getKey())  
136 - .setParameter("ts", entity.getTs())  
137 - .setParameter("dbl_v", entity.getDoubleValue())  
138 - .executeUpdate();  
139 - }  
140 } 85 }
dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/PsqlLatestInsertRepository.java renamed from dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/PsqlLatestInsertRepository.java
@@ -13,17 +13,17 @@ @@ -13,17 +13,17 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.dao.sqlts.ts; 16 +package org.thingsboard.server.dao.sqlts.latest;
17 17
18 import org.springframework.jdbc.core.BatchPreparedStatementSetter; 18 import org.springframework.jdbc.core.BatchPreparedStatementSetter;
19 import org.springframework.stereotype.Repository; 19 import org.springframework.stereotype.Repository;
20 import org.springframework.transaction.TransactionStatus; 20 import org.springframework.transaction.TransactionStatus;
21 import org.springframework.transaction.annotation.Transactional; 21 import org.springframework.transaction.annotation.Transactional;
22 import org.springframework.transaction.support.TransactionCallbackWithoutResult; 22 import org.springframework.transaction.support.TransactionCallbackWithoutResult;
23 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvLatestEntity;  
24 -import org.thingsboard.server.dao.sqlts.AbstractLatestInsertRepository;  
25 -import org.thingsboard.server.dao.util.PsqlDao;  
26 -import org.thingsboard.server.dao.util.SqlTsDao; 23 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
  24 +import org.thingsboard.server.dao.sqlts.AbstractInsertRepository;
  25 +import org.thingsboard.server.dao.sqlts.InsertLatestRepository;
  26 +import org.thingsboard.server.dao.util.PsqlTsAnyDao;
27 27
28 import java.sql.PreparedStatement; 28 import java.sql.PreparedStatement;
29 import java.sql.SQLException; 29 import java.sql.SQLException;
@@ -31,18 +31,11 @@ import java.sql.Types; @@ -31,18 +31,11 @@ import java.sql.Types;
31 import java.util.ArrayList; 31 import java.util.ArrayList;
32 import java.util.List; 32 import java.util.List;
33 33
34 -@SqlTsDao  
35 -@PsqlDao 34 +
  35 +@PsqlTsAnyDao
36 @Repository 36 @Repository
37 @Transactional 37 @Transactional
38 -public class PsqlLatestInsertRepository extends AbstractLatestInsertRepository {  
39 -  
40 - private static final String TS_KV_LATEST_CONSTRAINT = "(entity_type, entity_id, key)";  
41 -  
42 - private static final String INSERT_OR_UPDATE_BOOL_STATEMENT = getInsertOrUpdateStringPsql(TS_KV_LATEST_TABLE, TS_KV_LATEST_CONSTRAINT, BOOL_V, PSQL_ON_BOOL_VALUE_UPDATE_SET_NULLS);  
43 - private static final String INSERT_OR_UPDATE_STR_STATEMENT = getInsertOrUpdateStringPsql(TS_KV_LATEST_TABLE, TS_KV_LATEST_CONSTRAINT, STR_V, PSQL_ON_STR_VALUE_UPDATE_SET_NULLS);  
44 - private static final String INSERT_OR_UPDATE_LONG_STATEMENT = getInsertOrUpdateStringPsql(TS_KV_LATEST_TABLE, TS_KV_LATEST_CONSTRAINT, LONG_V, PSQL_ON_LONG_VALUE_UPDATE_SET_NULLS);  
45 - private static final String INSERT_OR_UPDATE_DBL_STATEMENT = getInsertOrUpdateStringPsql(TS_KV_LATEST_TABLE, TS_KV_LATEST_CONSTRAINT, DBL_V, PSQL_ON_DBL_VALUE_UPDATE_SET_NULLS); 38 +public class PsqlLatestInsertRepository extends AbstractInsertRepository implements InsertLatestRepository {
46 39
47 private static final String BATCH_UPDATE = 40 private static final String BATCH_UPDATE =
48 "UPDATE ts_kv_latest SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ? WHERE entity_type = ? AND entity_id = ? and key = ?"; 41 "UPDATE ts_kv_latest SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ? WHERE entity_type = ? AND entity_id = ? and key = ?";
@@ -53,11 +46,6 @@ public class PsqlLatestInsertRepository extends AbstractLatestInsertRepository { @@ -53,11 +46,6 @@ public class PsqlLatestInsertRepository extends AbstractLatestInsertRepository {
53 "ON CONFLICT (entity_type, entity_id, key) DO UPDATE SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;"; 46 "ON CONFLICT (entity_type, entity_id, key) DO UPDATE SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;";
54 47
55 @Override 48 @Override
56 - public void saveOrUpdate(TsKvLatestEntity entity) {  
57 - processSaveOrUpdate(entity, INSERT_OR_UPDATE_BOOL_STATEMENT, INSERT_OR_UPDATE_STR_STATEMENT, INSERT_OR_UPDATE_LONG_STATEMENT, INSERT_OR_UPDATE_DBL_STATEMENT);  
58 - }  
59 -  
60 - @Override  
61 public void saveOrUpdate(List<TsKvLatestEntity> entities) { 49 public void saveOrUpdate(List<TsKvLatestEntity> entities) {
62 transactionTemplate.execute(new TransactionCallbackWithoutResult() { 50 transactionTemplate.execute(new TransactionCallbackWithoutResult() {
63 @Override 51 @Override
@@ -160,48 +148,4 @@ public class PsqlLatestInsertRepository extends AbstractLatestInsertRepository { @@ -160,48 +148,4 @@ public class PsqlLatestInsertRepository extends AbstractLatestInsertRepository {
160 } 148 }
161 }); 149 });
162 } 150 }
163 -  
164 - @Override  
165 - protected void saveOrUpdateBoolean(TsKvLatestEntity entity, String query) {  
166 - entityManager.createNativeQuery(query)  
167 - .setParameter("entity_type", entity.getEntityType().name())  
168 - .setParameter("entity_id", entity.getEntityId())  
169 - .setParameter("key", entity.getKey())  
170 - .setParameter("ts", entity.getTs())  
171 - .setParameter("bool_v", entity.getBooleanValue())  
172 - .executeUpdate();  
173 - }  
174 -  
175 - @Override  
176 - protected void saveOrUpdateString(TsKvLatestEntity entity, String query) {  
177 - entityManager.createNativeQuery(query)  
178 - .setParameter("entity_type", entity.getEntityType().name())  
179 - .setParameter("entity_id", entity.getEntityId())  
180 - .setParameter("key", entity.getKey())  
181 - .setParameter("ts", entity.getTs())  
182 - .setParameter("str_v", replaceNullChars(entity.getStrValue()))  
183 - .executeUpdate();  
184 - }  
185 -  
186 - @Override  
187 - protected void saveOrUpdateLong(TsKvLatestEntity entity, String query) {  
188 - entityManager.createNativeQuery(query)  
189 - .setParameter("entity_type", entity.getEntityType().name())  
190 - .setParameter("entity_id", entity.getEntityId())  
191 - .setParameter("key", entity.getKey())  
192 - .setParameter("ts", entity.getTs())  
193 - .setParameter("long_v", entity.getLongValue())  
194 - .executeUpdate();  
195 - }  
196 -  
197 - @Override  
198 - protected void saveOrUpdateDouble(TsKvLatestEntity entity, String query) {  
199 - entityManager.createNativeQuery(query)  
200 - .setParameter("entity_type", entity.getEntityType().name())  
201 - .setParameter("entity_id", entity.getEntityId())  
202 - .setParameter("key", entity.getKey())  
203 - .setParameter("ts", entity.getTs())  
204 - .setParameter("dbl_v", entity.getDoubleValue())  
205 - .executeUpdate();  
206 - }  
207 } 151 }
dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/TsKvLatestRepository.java renamed from dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/TsKvLatestRepository.java
@@ -13,12 +13,12 @@ @@ -13,12 +13,12 @@
13 * See the License for the specific language governing permissions and 13 * See the License for the specific language governing permissions and
14 * limitations under the License. 14 * limitations under the License.
15 */ 15 */
16 -package org.thingsboard.server.dao.sqlts.ts; 16 +package org.thingsboard.server.dao.sqlts.latest;
17 17
18 import org.springframework.data.repository.CrudRepository; 18 import org.springframework.data.repository.CrudRepository;
19 import org.thingsboard.server.common.data.EntityType; 19 import org.thingsboard.server.common.data.EntityType;
20 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvLatestCompositeKey;  
21 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvLatestEntity; 20 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey;
  21 +import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity;
22 import org.thingsboard.server.dao.util.SqlDao; 22 import org.thingsboard.server.dao.util.SqlDao;
23 23
24 import java.util.List; 24 import java.util.List;
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts.psql;
  17 +
  18 +import com.google.common.util.concurrent.Futures;
  19 +import com.google.common.util.concurrent.ListenableFuture;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.hibernate.exception.ConstraintViolationException;
  22 +import org.springframework.beans.factory.annotation.Autowired;
  23 +import org.springframework.beans.factory.annotation.Value;
  24 +import org.springframework.data.domain.PageRequest;
  25 +import org.springframework.data.domain.Sort;
  26 +import org.springframework.stereotype.Component;
  27 +import org.thingsboard.server.common.data.id.EntityId;
  28 +import org.thingsboard.server.common.data.id.TenantId;
  29 +import org.thingsboard.server.common.data.kv.Aggregation;
  30 +import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
  31 +import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
  32 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  33 +import org.thingsboard.server.dao.DaoUtil;
  34 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;
  35 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey;
  36 +import org.thingsboard.server.dao.model.sqlts.psql.TsKvEntity;
  37 +import org.thingsboard.server.dao.sqlts.AbstractSimpleSqlTimeseriesDao;
  38 +import org.thingsboard.server.dao.sqlts.EntityContainer;
  39 +import org.thingsboard.server.dao.sqlts.dictionary.TsKvDictionaryRepository;
  40 +import org.thingsboard.server.dao.timeseries.PsqlPartition;
  41 +import org.thingsboard.server.dao.timeseries.SqlTsPartitionDate;
  42 +import org.thingsboard.server.dao.timeseries.TimeseriesDao;
  43 +import org.thingsboard.server.dao.util.PsqlDao;
  44 +import org.thingsboard.server.dao.util.SqlTsDao;
  45 +
  46 +import java.time.Instant;
  47 +import java.time.LocalDateTime;
  48 +import java.time.ZoneOffset;
  49 +import java.util.ArrayList;
  50 +import java.util.List;
  51 +import java.util.Optional;
  52 +import java.util.Set;
  53 +import java.util.concurrent.CompletableFuture;
  54 +import java.util.concurrent.ConcurrentHashMap;
  55 +import java.util.concurrent.ConcurrentMap;
  56 +import java.util.concurrent.locks.ReentrantLock;
  57 +
  58 +import static org.thingsboard.server.dao.timeseries.SqlTsPartitionDate.EPOCH_START;
  59 +
  60 +
  61 +@Component
  62 +@Slf4j
  63 +@SqlTsDao
  64 +@PsqlDao
  65 +public class JpaPsqlTimeseriesDao extends AbstractSimpleSqlTimeseriesDao<TsKvEntity> implements TimeseriesDao {
  66 +
  67 + private final ConcurrentMap<String, Integer> tsKvDictionaryMap = new ConcurrentHashMap<>();
  68 + private final Set<PsqlPartition> partitions = ConcurrentHashMap.newKeySet();
  69 +
  70 + private static final ReentrantLock tsCreationLock = new ReentrantLock();
  71 + private static final ReentrantLock partitionCreationLock = new ReentrantLock();
  72 +
  73 + @Autowired
  74 + private TsKvDictionaryRepository dictionaryRepository;
  75 +
  76 + @Autowired
  77 + private TsKvPsqlRepository tsKvRepository;
  78 +
  79 + @Autowired
  80 + private PsqlPartitioningRepository partitioningRepository;
  81 +
  82 + private SqlTsPartitionDate tsFormat;
  83 +
  84 + @Value("${sql.ts_key_value_partitioning}")
  85 + private String partitioning;
  86 +
  87 + @Override
  88 + protected void init() {
  89 + super.init();
  90 + Optional<SqlTsPartitionDate> partition = SqlTsPartitionDate.parse(partitioning);
  91 + if (partition.isPresent()) {
  92 + tsFormat = partition.get();
  93 + } else {
  94 + log.warn("Incorrect configuration of partitioning {}", partitioning);
  95 + throw new RuntimeException("Failed to parse partitioning property: " + partitioning + "!");
  96 + }
  97 + }
  98 +
  99 + @Override
  100 + public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
  101 + return processFindAllAsync(tenantId, entityId, queries);
  102 + }
  103 +
  104 + @Override
  105 + public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
  106 + String strKey = tsKvEntry.getKey();
  107 + Integer keyId = getOrSaveKeyId(strKey);
  108 + TsKvEntity entity = new TsKvEntity();
  109 + entity.setEntityId(entityId.getId());
  110 + entity.setTs(tsKvEntry.getTs());
  111 + entity.setKey(keyId);
  112 + entity.setStrValue(tsKvEntry.getStrValue().orElse(null));
  113 + entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
  114 + entity.setLongValue(tsKvEntry.getLongValue().orElse(null));
  115 + entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
  116 + PsqlPartition psqlPartition = toPartition(tsKvEntry.getTs());
  117 + savePartition(psqlPartition);
  118 + log.trace("Saving entity: {}", entity);
  119 + return tsQueue.add(new EntityContainer(entity, psqlPartition.getPartitionDate()));
  120 + }
  121 +
  122 + @Override
  123 + public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  124 + return service.submit(() -> {
  125 + String strKey = query.getKey();
  126 + Integer keyId = getOrSaveKeyId(strKey);
  127 + tsKvRepository.delete(
  128 + entityId.getId(),
  129 + keyId,
  130 + query.getStartTs(),
  131 + query.getEndTs());
  132 + return null;
  133 + });
  134 + }
  135 +
  136 + @Override
  137 + public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  138 + return getRemoveLatestFuture(tenantId, entityId, query);
  139 + }
  140 +
  141 + @Override
  142 + public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
  143 + return getSaveLatestFuture(entityId, tsKvEntry);
  144 + }
  145 +
  146 + @Override
  147 + public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
  148 + return getFindLatestFuture(entityId, key);
  149 + }
  150 +
  151 + @Override
  152 + public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
  153 + return getFindAllLatestFuture(entityId);
  154 + }
  155 +
  156 + @Override
  157 + public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
  158 + return Futures.immediateFuture(null);
  159 + }
  160 +
  161 + @Override
  162 + public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  163 + return Futures.immediateFuture(null);
  164 + }
  165 +
  166 + protected ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(EntityId entityId, String key, long startTs, long endTs, long ts, Aggregation aggregation) {
  167 + List<CompletableFuture<TsKvEntity>> entitiesFutures = new ArrayList<>();
  168 + switchAgregation(entityId, key, startTs, endTs, aggregation, entitiesFutures);
  169 + return Futures.transform(setFutures(entitiesFutures), entity -> {
  170 + if (entity != null && entity.isNotEmpty()) {
  171 + entity.setEntityId(entityId.getId());
  172 + entity.setStrKey(key);
  173 + entity.setTs(ts);
  174 + return Optional.of(DaoUtil.getData(entity));
  175 + } else {
  176 + return Optional.empty();
  177 + }
  178 + });
  179 + }
  180 +
  181 + protected ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {
  182 + Integer keyId = getOrSaveKeyId(query.getKey());
  183 + List<TsKvEntity> tsKvEntities = tsKvRepository.findAllWithLimit(
  184 + entityId.getId(),
  185 + keyId,
  186 + query.getStartTs(),
  187 + query.getEndTs(),
  188 + new PageRequest(0, query.getLimit(),
  189 + new Sort(Sort.Direction.fromString(
  190 + query.getOrderBy()), "ts")));
  191 + tsKvEntities.forEach(tsKvEntity -> tsKvEntity.setStrKey(query.getKey()));
  192 + return Futures.immediateFuture(DaoUtil.convertDataList(tsKvEntities));
  193 + }
  194 +
  195 + protected void findCount(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures) {
  196 + Integer keyId = getOrSaveKeyId(key);
  197 + entitiesFutures.add(tsKvRepository.findCount(
  198 + entityId.getId(),
  199 + keyId,
  200 + startTs,
  201 + endTs));
  202 + }
  203 +
  204 + protected void findSum(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures) {
  205 + Integer keyId = getOrSaveKeyId(key);
  206 + entitiesFutures.add(tsKvRepository.findSum(
  207 + entityId.getId(),
  208 + keyId,
  209 + startTs,
  210 + endTs));
  211 + }
  212 +
  213 + protected void findMin(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures) {
  214 + Integer keyId = getOrSaveKeyId(key);
  215 + entitiesFutures.add(tsKvRepository.findStringMin(
  216 + entityId.getId(),
  217 + keyId,
  218 + startTs,
  219 + endTs));
  220 + entitiesFutures.add(tsKvRepository.findNumericMin(
  221 + entityId.getId(),
  222 + keyId,
  223 + startTs,
  224 + endTs));
  225 + }
  226 +
  227 + protected void findMax(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures) {
  228 + Integer keyId = getOrSaveKeyId(key);
  229 + entitiesFutures.add(tsKvRepository.findStringMax(
  230 + entityId.getId(),
  231 + keyId,
  232 + startTs,
  233 + endTs));
  234 + entitiesFutures.add(tsKvRepository.findNumericMax(
  235 + entityId.getId(),
  236 + keyId,
  237 + startTs,
  238 + endTs));
  239 + }
  240 +
  241 + protected void findAvg(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures) {
  242 + Integer keyId = getOrSaveKeyId(key);
  243 + entitiesFutures.add(tsKvRepository.findAvg(
  244 + entityId.getId(),
  245 + keyId,
  246 + startTs,
  247 + endTs));
  248 + }
  249 +
  250 + private Integer getOrSaveKeyId(String strKey) {
  251 + Integer keyId = tsKvDictionaryMap.get(strKey);
  252 + if (keyId == null) {
  253 + Optional<TsKvDictionary> tsKvDictionaryOptional;
  254 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  255 + if (!tsKvDictionaryOptional.isPresent()) {
  256 + tsCreationLock.lock();
  257 + try {
  258 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  259 + if (!tsKvDictionaryOptional.isPresent()) {
  260 + TsKvDictionary tsKvDictionary = new TsKvDictionary();
  261 + tsKvDictionary.setKey(strKey);
  262 + try {
  263 + TsKvDictionary saved = dictionaryRepository.save(tsKvDictionary);
  264 + tsKvDictionaryMap.put(saved.getKey(), saved.getKeyId());
  265 + keyId = saved.getKeyId();
  266 + } catch (ConstraintViolationException e) {
  267 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  268 + TsKvDictionary dictionary = tsKvDictionaryOptional.orElseThrow(() -> new RuntimeException("Failed to get TsKvDictionary entity from DB!"));
  269 + tsKvDictionaryMap.put(dictionary.getKey(), dictionary.getKeyId());
  270 + keyId = dictionary.getKeyId();
  271 + }
  272 + } else {
  273 + keyId = tsKvDictionaryOptional.get().getKeyId();
  274 + }
  275 + } finally {
  276 + tsCreationLock.unlock();
  277 + }
  278 + } else {
  279 + keyId = tsKvDictionaryOptional.get().getKeyId();
  280 + tsKvDictionaryMap.put(strKey, keyId);
  281 + }
  282 + }
  283 + return keyId;
  284 + }
  285 +
  286 + private void savePartition(PsqlPartition psqlPartition) {
  287 + if (!partitions.contains(psqlPartition)) {
  288 + partitionCreationLock.lock();
  289 + try {
  290 + log.trace("Saving partition: {}", psqlPartition);
  291 + partitioningRepository.save(psqlPartition);
  292 + log.trace("Adding partition to Set: {}", psqlPartition);
  293 + partitions.add(psqlPartition);
  294 + } finally {
  295 + partitionCreationLock.unlock();
  296 + }
  297 + }
  298 + }
  299 +
  300 + private PsqlPartition toPartition(long ts) {
  301 + LocalDateTime time = LocalDateTime.ofInstant(Instant.ofEpochMilli(ts), ZoneOffset.UTC);
  302 + LocalDateTime localDateTimeStart = tsFormat.trancateTo(time);
  303 + if (localDateTimeStart == SqlTsPartitionDate.EPOCH_START) {
  304 + return new PsqlPartition(toMills(EPOCH_START), Long.MAX_VALUE, tsFormat.getPattern());
  305 + } else {
  306 + LocalDateTime localDateTimeEnd = tsFormat.plusTo(localDateTimeStart);
  307 + return new PsqlPartition(toMills(localDateTimeStart), toMills(localDateTimeEnd), tsFormat.getPattern());
  308 + }
  309 + }
  310 +
  311 + private long toMills(LocalDateTime time) { return time.toInstant(ZoneOffset.UTC).toEpochMilli(); }
  312 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts.psql;
  17 +
  18 +import org.springframework.stereotype.Repository;
  19 +import org.springframework.transaction.annotation.Transactional;
  20 +import org.thingsboard.server.dao.timeseries.PsqlPartition;
  21 +import org.thingsboard.server.dao.util.PsqlDao;
  22 +import org.thingsboard.server.dao.util.SqlTsDao;
  23 +
  24 +import javax.persistence.EntityManager;
  25 +import javax.persistence.PersistenceContext;
  26 +
  27 +@SqlTsDao
  28 +@PsqlDao
  29 +@Repository
  30 +@Transactional
  31 +public class PsqlPartitioningRepository {
  32 +
  33 + @PersistenceContext
  34 + private EntityManager entityManager;
  35 +
  36 + public void save(PsqlPartition partition) {
  37 + entityManager.createNativeQuery(partition.getQuery())
  38 + .executeUpdate();
  39 + }
  40 +
  41 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts.psql;
  17 +
  18 +import org.springframework.jdbc.core.BatchPreparedStatementSetter;
  19 +import org.springframework.stereotype.Repository;
  20 +import org.springframework.transaction.annotation.Transactional;
  21 +import org.thingsboard.server.dao.model.sqlts.psql.TsKvEntity;
  22 +import org.thingsboard.server.dao.sqlts.AbstractInsertRepository;
  23 +import org.thingsboard.server.dao.sqlts.EntityContainer;
  24 +import org.thingsboard.server.dao.sqlts.InsertTsRepository;
  25 +import org.thingsboard.server.dao.util.PsqlDao;
  26 +import org.thingsboard.server.dao.util.SqlTsDao;
  27 +
  28 +import java.sql.PreparedStatement;
  29 +import java.sql.SQLException;
  30 +import java.sql.Types;
  31 +import java.util.ArrayList;
  32 +import java.util.HashMap;
  33 +import java.util.List;
  34 +import java.util.Map;
  35 +
  36 +@SqlTsDao
  37 +@PsqlDao
  38 +@Repository
  39 +@Transactional
  40 +public class PsqlTimeseriesInsertRepository extends AbstractInsertRepository implements InsertTsRepository<TsKvEntity> {
  41 +
  42 + private static final String INSERT_INTO_TS_KV = "INSERT INTO ts_kv_";
  43 +
  44 + private static final String VALUES_ON_CONFLICT_DO_UPDATE = " (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) VALUES (?, ?, ?, ?, ?, ?, ?) " +
  45 + "ON CONFLICT (entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;";
  46 +
  47 + @Override
  48 + public void saveOrUpdate(List<EntityContainer<TsKvEntity>> entities) {
  49 + Map<String, List<TsKvEntity>> partitionMap = new HashMap<>();
  50 + for (EntityContainer<TsKvEntity> entityContainer : entities) {
  51 + List<TsKvEntity> tsKvEntities = partitionMap.computeIfAbsent(entityContainer.getPartitionDate(), k -> new ArrayList<>());
  52 + tsKvEntities.add(entityContainer.getEntity());
  53 + }
  54 + partitionMap.forEach((partition, entries) -> jdbcTemplate.batchUpdate(getInsertOrUpdateQuery(partition), new BatchPreparedStatementSetter() {
  55 + @Override
  56 + public void setValues(PreparedStatement ps, int i) throws SQLException {
  57 + TsKvEntity tsKvEntity = entries.get(i);
  58 + ps.setObject(1, tsKvEntity.getEntityId());
  59 + ps.setInt(2, tsKvEntity.getKey());
  60 + ps.setLong(3, tsKvEntity.getTs());
  61 +
  62 + if (tsKvEntity.getBooleanValue() != null) {
  63 + ps.setBoolean(4, tsKvEntity.getBooleanValue());
  64 + ps.setBoolean(8, tsKvEntity.getBooleanValue());
  65 + } else {
  66 + ps.setNull(4, Types.BOOLEAN);
  67 + ps.setNull(8, Types.BOOLEAN);
  68 + }
  69 +
  70 + ps.setString(5, replaceNullChars(tsKvEntity.getStrValue()));
  71 + ps.setString(9, replaceNullChars(tsKvEntity.getStrValue()));
  72 +
  73 +
  74 + if (tsKvEntity.getLongValue() != null) {
  75 + ps.setLong(6, tsKvEntity.getLongValue());
  76 + ps.setLong(10, tsKvEntity.getLongValue());
  77 + } else {
  78 + ps.setNull(6, Types.BIGINT);
  79 + ps.setNull(10, Types.BIGINT);
  80 + }
  81 +
  82 + if (tsKvEntity.getDoubleValue() != null) {
  83 + ps.setDouble(7, tsKvEntity.getDoubleValue());
  84 + ps.setDouble(11, tsKvEntity.getDoubleValue());
  85 + } else {
  86 + ps.setNull(7, Types.DOUBLE);
  87 + ps.setNull(11, Types.DOUBLE);
  88 + }
  89 + }
  90 +
  91 + @Override
  92 + public int getBatchSize() {
  93 + return entries.size();
  94 + }
  95 + }));
  96 + }
  97 +
  98 + private String getInsertOrUpdateQuery(String partitionDate) {
  99 + return INSERT_INTO_TS_KV + partitionDate + VALUES_ON_CONFLICT_DO_UPDATE;
  100 + }
  101 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.sqlts.psql;
  17 +
  18 +import org.springframework.data.domain.Pageable;
  19 +import org.springframework.data.jpa.repository.Modifying;
  20 +import org.springframework.data.jpa.repository.Query;
  21 +import org.springframework.data.repository.CrudRepository;
  22 +import org.springframework.data.repository.query.Param;
  23 +import org.springframework.scheduling.annotation.Async;
  24 +import org.springframework.transaction.annotation.Transactional;
  25 +import org.thingsboard.server.dao.model.sqlts.psql.TsKvCompositeKey;
  26 +import org.thingsboard.server.dao.model.sqlts.psql.TsKvEntity;
  27 +import org.thingsboard.server.dao.util.SqlDao;
  28 +
  29 +import java.util.List;
  30 +import java.util.UUID;
  31 +import java.util.concurrent.CompletableFuture;
  32 +
  33 +@SqlDao
  34 +public interface TsKvPsqlRepository extends CrudRepository<TsKvEntity, TsKvCompositeKey> {
  35 +
  36 + @Query("SELECT tskv FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " +
  37 + "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
  38 + List<TsKvEntity> findAllWithLimit(@Param("entityId") UUID entityId,
  39 + @Param("entityKey") int key,
  40 + @Param("startTs") long startTs,
  41 + @Param("endTs") long endTs,
  42 + Pageable pageable);
  43 +
  44 + @Transactional
  45 + @Modifying
  46 + @Query("DELETE FROM TsKvEntity tskv WHERE tskv.entityId = :entityId " +
  47 + "AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
  48 + void delete(@Param("entityId") UUID entityId,
  49 + @Param("entityKey") int key,
  50 + @Param("startTs") long startTs,
  51 + @Param("endTs") long endTs);
  52 +
  53 + @Async
  54 + @Query("SELECT new TsKvEntity(MAX(tskv.strValue)) FROM TsKvEntity tskv " +
  55 + "WHERE tskv.strValue IS NOT NULL " +
  56 + "AND tskv.entityId = :entityId AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
  57 + CompletableFuture<TsKvEntity> findStringMax(@Param("entityId") UUID entityId,
  58 + @Param("entityKey") int entityKey,
  59 + @Param("startTs") long startTs,
  60 + @Param("endTs") long endTs);
  61 +
  62 + @Async
  63 + @Query("SELECT new TsKvEntity(MAX(COALESCE(tskv.longValue, -9223372036854775807)), " +
  64 + "MAX(COALESCE(tskv.doubleValue, -1.79769E+308)), " +
  65 + "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " +
  66 + "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END), " +
  67 + "'MAX') FROM TsKvEntity tskv " +
  68 + "WHERE tskv.entityId = :entityId AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
  69 + CompletableFuture<TsKvEntity> findNumericMax(@Param("entityId") UUID entityId,
  70 + @Param("entityKey") int entityKey,
  71 + @Param("startTs") long startTs,
  72 + @Param("endTs") long endTs);
  73 +
  74 +
  75 + @Async
  76 + @Query("SELECT new TsKvEntity(MIN(tskv.strValue)) FROM TsKvEntity tskv " +
  77 + "WHERE tskv.strValue IS NOT NULL " +
  78 + "AND tskv.entityId = :entityId AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
  79 + CompletableFuture<TsKvEntity> findStringMin(@Param("entityId") UUID entityId,
  80 + @Param("entityKey") int entityKey,
  81 + @Param("startTs") long startTs,
  82 + @Param("endTs") long endTs);
  83 +
  84 + @Async
  85 + @Query("SELECT new TsKvEntity(MIN(COALESCE(tskv.longValue, 9223372036854775807)), " +
  86 + "MIN(COALESCE(tskv.doubleValue, 1.79769E+308)), " +
  87 + "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " +
  88 + "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END), " +
  89 + "'MIN') FROM TsKvEntity tskv " +
  90 + "WHERE tskv.entityId = :entityId AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
  91 + CompletableFuture<TsKvEntity> findNumericMin(
  92 + @Param("entityId") UUID entityId,
  93 + @Param("entityKey") int entityKey,
  94 + @Param("startTs") long startTs,
  95 + @Param("endTs") long endTs);
  96 +
  97 + @Async
  98 + @Query("SELECT new TsKvEntity(SUM(CASE WHEN tskv.booleanValue IS NULL THEN 0 ELSE 1 END), " +
  99 + "SUM(CASE WHEN tskv.strValue IS NULL THEN 0 ELSE 1 END), " +
  100 + "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " +
  101 + "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END)) FROM TsKvEntity tskv " +
  102 + "WHERE tskv.entityId = :entityId AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
  103 + CompletableFuture<TsKvEntity> findCount(@Param("entityId") UUID entityId,
  104 + @Param("entityKey") int entityKey,
  105 + @Param("startTs") long startTs,
  106 + @Param("endTs") long endTs);
  107 +
  108 + @Async
  109 + @Query("SELECT new TsKvEntity(SUM(COALESCE(tskv.longValue, 0)), " +
  110 + "SUM(COALESCE(tskv.doubleValue, 0.0)), " +
  111 + "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " +
  112 + "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END), " +
  113 + "'AVG') FROM TsKvEntity tskv " +
  114 + "WHERE tskv.entityId = :entityId AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
  115 + CompletableFuture<TsKvEntity> findAvg(@Param("entityId") UUID entityId,
  116 + @Param("entityKey") int entityKey,
  117 + @Param("startTs") long startTs,
  118 + @Param("endTs") long endTs);
  119 +
  120 + @Async
  121 + @Query("SELECT new TsKvEntity(SUM(COALESCE(tskv.longValue, 0)), " +
  122 + "SUM(COALESCE(tskv.doubleValue, 0.0)), " +
  123 + "SUM(CASE WHEN tskv.longValue IS NULL THEN 0 ELSE 1 END), " +
  124 + "SUM(CASE WHEN tskv.doubleValue IS NULL THEN 0 ELSE 1 END), " +
  125 + "'SUM') FROM TsKvEntity tskv " +
  126 + "WHERE tskv.entityId = :entityId AND tskv.key = :entityKey AND tskv.ts > :startTs AND tskv.ts <= :endTs")
  127 + CompletableFuture<TsKvEntity> findSum(@Param("entityId") UUID entityId,
  128 + @Param("entityKey") int entityKey,
  129 + @Param("startTs") long startTs,
  130 + @Param("endTs") long endTs);
  131 +
  132 +}
@@ -23,6 +23,7 @@ import org.thingsboard.server.dao.util.TimescaleDBTsDao; @@ -23,6 +23,7 @@ import org.thingsboard.server.dao.util.TimescaleDBTsDao;
23 import javax.persistence.EntityManager; 23 import javax.persistence.EntityManager;
24 import javax.persistence.PersistenceContext; 24 import javax.persistence.PersistenceContext;
25 import java.util.List; 25 import java.util.List;
  26 +import java.util.UUID;
26 import java.util.concurrent.CompletableFuture; 27 import java.util.concurrent.CompletableFuture;
27 28
28 @Repository 29 @Repository
@@ -36,7 +37,7 @@ public class AggregationRepository { @@ -36,7 +37,7 @@ public class AggregationRepository {
36 public static final String FIND_COUNT = "findCount"; 37 public static final String FIND_COUNT = "findCount";
37 38
38 39
39 - public static final String FROM_WHERE_CLAUSE = "FROM tenant_ts_kv tskv WHERE tskv.tenant_id = cast(:tenantId AS varchar) AND tskv.entity_id = cast(:entityId AS varchar) AND tskv.key= cast(:entityKey AS varchar) AND tskv.ts > :startTs AND tskv.ts <= :endTs GROUP BY tskv.tenant_id, tskv.entity_id, tskv.key, tsBucket ORDER BY tskv.tenant_id, tskv.entity_id, tskv.key, tsBucket"; 40 + public static final String FROM_WHERE_CLAUSE = "FROM tenant_ts_kv tskv WHERE tskv.tenant_id = cast(:tenantId AS uuid) AND tskv.entity_id = cast(:entityId AS uuid) AND tskv.key= cast(:entityKey AS int) AND tskv.ts > :startTs AND tskv.ts <= :endTs GROUP BY tskv.tenant_id, tskv.entity_id, tskv.key, tsBucket ORDER BY tskv.tenant_id, tskv.entity_id, tskv.key, tsBucket";
40 41
41 public static final String FIND_AVG_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, 'AVG' AS aggType "; 42 public static final String FIND_AVG_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, 'AVG' AS aggType ";
42 43
@@ -52,41 +53,41 @@ public class AggregationRepository { @@ -52,41 +53,41 @@ public class AggregationRepository {
52 private EntityManager entityManager; 53 private EntityManager entityManager;
53 54
54 @Async 55 @Async
55 - public CompletableFuture<List<TimescaleTsKvEntity>> findAvg(String tenantId, String entityId, String entityKey, long timeBucket, long startTs, long endTs) { 56 + public CompletableFuture<List<TimescaleTsKvEntity>> findAvg(UUID tenantId, UUID entityId, int entityKey, long timeBucket, long startTs, long endTs) {
56 @SuppressWarnings("unchecked") 57 @SuppressWarnings("unchecked")
57 List<TimescaleTsKvEntity> resultList = getResultList(tenantId, entityId, entityKey, timeBucket, startTs, endTs, FIND_AVG); 58 List<TimescaleTsKvEntity> resultList = getResultList(tenantId, entityId, entityKey, timeBucket, startTs, endTs, FIND_AVG);
58 return CompletableFuture.supplyAsync(() -> resultList); 59 return CompletableFuture.supplyAsync(() -> resultList);
59 } 60 }
60 61
61 @Async 62 @Async
62 - public CompletableFuture<List<TimescaleTsKvEntity>> findMax(String tenantId, String entityId, String entityKey, long timeBucket, long startTs, long endTs) { 63 + public CompletableFuture<List<TimescaleTsKvEntity>> findMax(UUID tenantId, UUID entityId, int entityKey, long timeBucket, long startTs, long endTs) {
63 @SuppressWarnings("unchecked") 64 @SuppressWarnings("unchecked")
64 List<TimescaleTsKvEntity> resultList = getResultList(tenantId, entityId, entityKey, timeBucket, startTs, endTs, FIND_MAX); 65 List<TimescaleTsKvEntity> resultList = getResultList(tenantId, entityId, entityKey, timeBucket, startTs, endTs, FIND_MAX);
65 return CompletableFuture.supplyAsync(() -> resultList); 66 return CompletableFuture.supplyAsync(() -> resultList);
66 } 67 }
67 68
68 @Async 69 @Async
69 - public CompletableFuture<List<TimescaleTsKvEntity>> findMin(String tenantId, String entityId, String entityKey, long timeBucket, long startTs, long endTs) { 70 + public CompletableFuture<List<TimescaleTsKvEntity>> findMin(UUID tenantId, UUID entityId, int entityKey, long timeBucket, long startTs, long endTs) {
70 @SuppressWarnings("unchecked") 71 @SuppressWarnings("unchecked")
71 List<TimescaleTsKvEntity> resultList = getResultList(tenantId, entityId, entityKey, timeBucket, startTs, endTs, FIND_MIN); 72 List<TimescaleTsKvEntity> resultList = getResultList(tenantId, entityId, entityKey, timeBucket, startTs, endTs, FIND_MIN);
72 return CompletableFuture.supplyAsync(() -> resultList); 73 return CompletableFuture.supplyAsync(() -> resultList);
73 } 74 }
74 75
75 @Async 76 @Async
76 - public CompletableFuture<List<TimescaleTsKvEntity>> findSum(String tenantId, String entityId, String entityKey, long timeBucket, long startTs, long endTs) { 77 + public CompletableFuture<List<TimescaleTsKvEntity>> findSum(UUID tenantId, UUID entityId, int entityKey, long timeBucket, long startTs, long endTs) {
77 @SuppressWarnings("unchecked") 78 @SuppressWarnings("unchecked")
78 List<TimescaleTsKvEntity> resultList = getResultList(tenantId, entityId, entityKey, timeBucket, startTs, endTs, FIND_SUM); 79 List<TimescaleTsKvEntity> resultList = getResultList(tenantId, entityId, entityKey, timeBucket, startTs, endTs, FIND_SUM);
79 return CompletableFuture.supplyAsync(() -> resultList); 80 return CompletableFuture.supplyAsync(() -> resultList);
80 } 81 }
81 82
82 @Async 83 @Async
83 - public CompletableFuture<List<TimescaleTsKvEntity>> findCount(String tenantId, String entityId, String entityKey, long timeBucket, long startTs, long endTs) { 84 + public CompletableFuture<List<TimescaleTsKvEntity>> findCount(UUID tenantId, UUID entityId, int entityKey, long timeBucket, long startTs, long endTs) {
84 @SuppressWarnings("unchecked") 85 @SuppressWarnings("unchecked")
85 List<TimescaleTsKvEntity> resultList = getResultList(tenantId, entityId, entityKey, timeBucket, startTs, endTs, FIND_COUNT); 86 List<TimescaleTsKvEntity> resultList = getResultList(tenantId, entityId, entityKey, timeBucket, startTs, endTs, FIND_COUNT);
86 return CompletableFuture.supplyAsync(() -> resultList); 87 return CompletableFuture.supplyAsync(() -> resultList);
87 } 88 }
88 89
89 - private List getResultList(String tenantId, String entityId, String entityKey, long timeBucket, long startTs, long endTs, String query) { 90 + private List getResultList(UUID tenantId, UUID entityId, int entityKey, long timeBucket, long startTs, long endTs, String query) {
90 return entityManager.createNamedQuery(query) 91 return entityManager.createNamedQuery(query)
91 .setParameter("tenantId", tenantId) 92 .setParameter("tenantId", tenantId)
92 .setParameter("entityId", entityId) 93 .setParameter("entityId", entityId)
@@ -19,7 +19,9 @@ import org.springframework.jdbc.core.BatchPreparedStatementSetter; @@ -19,7 +19,9 @@ import org.springframework.jdbc.core.BatchPreparedStatementSetter;
19 import org.springframework.stereotype.Repository; 19 import org.springframework.stereotype.Repository;
20 import org.springframework.transaction.annotation.Transactional; 20 import org.springframework.transaction.annotation.Transactional;
21 import org.thingsboard.server.dao.model.sqlts.timescale.TimescaleTsKvEntity; 21 import org.thingsboard.server.dao.model.sqlts.timescale.TimescaleTsKvEntity;
22 -import org.thingsboard.server.dao.sqlts.AbstractTimeseriesInsertRepository; 22 +import org.thingsboard.server.dao.sqlts.AbstractInsertRepository;
  23 +import org.thingsboard.server.dao.sqlts.EntityContainer;
  24 +import org.thingsboard.server.dao.sqlts.InsertTsRepository;
23 import org.thingsboard.server.dao.util.PsqlDao; 25 import org.thingsboard.server.dao.util.PsqlDao;
24 import org.thingsboard.server.dao.util.TimescaleDBTsDao; 26 import org.thingsboard.server.dao.util.TimescaleDBTsDao;
25 27
@@ -32,35 +34,21 @@ import java.util.List; @@ -32,35 +34,21 @@ import java.util.List;
32 @PsqlDao 34 @PsqlDao
33 @Repository 35 @Repository
34 @Transactional 36 @Transactional
35 -public class TimescaleInsertRepository extends AbstractTimeseriesInsertRepository<TimescaleTsKvEntity> {  
36 -  
37 - private static final String INSERT_OR_UPDATE_BOOL_STATEMENT = getInsertOrUpdateString(BOOL_V, PSQL_ON_BOOL_VALUE_UPDATE_SET_NULLS);  
38 - private static final String INSERT_OR_UPDATE_STR_STATEMENT = getInsertOrUpdateString(STR_V, PSQL_ON_STR_VALUE_UPDATE_SET_NULLS);  
39 - private static final String INSERT_OR_UPDATE_LONG_STATEMENT = getInsertOrUpdateString(LONG_V, PSQL_ON_LONG_VALUE_UPDATE_SET_NULLS);  
40 - private static final String INSERT_OR_UPDATE_DBL_STATEMENT = getInsertOrUpdateString(DBL_V, PSQL_ON_DBL_VALUE_UPDATE_SET_NULLS);  
41 -  
42 - private static final String BATCH_UPDATE =  
43 - "UPDATE tenant_ts_kv SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ? WHERE entity_type = ? AND entity_id = ? and key = ? and ts = ?";  
44 - 37 +public class TimescaleInsertRepository extends AbstractInsertRepository implements InsertTsRepository<TimescaleTsKvEntity> {
45 38
46 private static final String INSERT_OR_UPDATE = 39 private static final String INSERT_OR_UPDATE =
47 "INSERT INTO tenant_ts_kv (tenant_id, entity_id, key, ts, bool_v, str_v, long_v, dbl_v) VALUES(?, ?, ?, ?, ?, ?, ?, ?) " + 40 "INSERT INTO tenant_ts_kv (tenant_id, entity_id, key, ts, bool_v, str_v, long_v, dbl_v) VALUES(?, ?, ?, ?, ?, ?, ?, ?) " +
48 "ON CONFLICT (tenant_id, entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;"; 41 "ON CONFLICT (tenant_id, entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;";
49 42
50 @Override 43 @Override
51 - public void saveOrUpdate(TimescaleTsKvEntity entity) {  
52 - processSaveOrUpdate(entity, INSERT_OR_UPDATE_BOOL_STATEMENT, INSERT_OR_UPDATE_STR_STATEMENT, INSERT_OR_UPDATE_LONG_STATEMENT, INSERT_OR_UPDATE_DBL_STATEMENT);  
53 - }  
54 -  
55 - @Override  
56 - public void saveOrUpdate(List<TimescaleTsKvEntity> entities) { 44 + public void saveOrUpdate(List<EntityContainer<TimescaleTsKvEntity>> entities) {
57 jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() { 45 jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() {
58 @Override 46 @Override
59 public void setValues(PreparedStatement ps, int i) throws SQLException { 47 public void setValues(PreparedStatement ps, int i) throws SQLException {
60 - TimescaleTsKvEntity tsKvEntity = entities.get(i);  
61 - ps.setString(1, tsKvEntity.getTenantId());  
62 - ps.setString(2, tsKvEntity.getEntityId());  
63 - ps.setString(3, tsKvEntity.getKey()); 48 + TimescaleTsKvEntity tsKvEntity = entities.get(i).getEntity();
  49 + ps.setObject(1, tsKvEntity.getTenantId());
  50 + ps.setObject(2, tsKvEntity.getEntityId());
  51 + ps.setInt(3, tsKvEntity.getKey());
64 ps.setLong(4, tsKvEntity.getTs()); 52 ps.setLong(4, tsKvEntity.getTs());
65 53
66 if (tsKvEntity.getBooleanValue() != null) { 54 if (tsKvEntity.getBooleanValue() != null) {
@@ -98,52 +86,4 @@ public class TimescaleInsertRepository extends AbstractTimeseriesInsertRepositor @@ -98,52 +86,4 @@ public class TimescaleInsertRepository extends AbstractTimeseriesInsertRepositor
98 } 86 }
99 }); 87 });
100 } 88 }
101 -  
102 - @Override  
103 - protected void saveOrUpdateBoolean(TimescaleTsKvEntity entity, String query) {  
104 - entityManager.createNativeQuery(query)  
105 - .setParameter("tenant_id", entity.getTenantId())  
106 - .setParameter("entity_id", entity.getEntityId())  
107 - .setParameter("key", entity.getKey())  
108 - .setParameter("ts", entity.getTs())  
109 - .setParameter("bool_v", entity.getBooleanValue())  
110 - .executeUpdate();  
111 - }  
112 -  
113 - @Override  
114 - protected void saveOrUpdateString(TimescaleTsKvEntity entity, String query) {  
115 - entityManager.createNativeQuery(query)  
116 - .setParameter("tenant_id", entity.getTenantId())  
117 - .setParameter("entity_id", entity.getEntityId())  
118 - .setParameter("key", entity.getKey())  
119 - .setParameter("ts", entity.getTs())  
120 - .setParameter("str_v", replaceNullChars(entity.getStrValue()))  
121 - .executeUpdate();  
122 - }  
123 -  
124 - @Override  
125 - protected void saveOrUpdateLong(TimescaleTsKvEntity entity, String query) {  
126 - entityManager.createNativeQuery(query)  
127 - .setParameter("tenant_id", entity.getTenantId())  
128 - .setParameter("entity_id", entity.getEntityId())  
129 - .setParameter("key", entity.getKey())  
130 - .setParameter("ts", entity.getTs())  
131 - .setParameter("long_v", entity.getLongValue())  
132 - .executeUpdate();  
133 - }  
134 -  
135 - @Override  
136 - protected void saveOrUpdateDouble(TimescaleTsKvEntity entity, String query) {  
137 - entityManager.createNativeQuery(query)  
138 - .setParameter("tenant_id", entity.getTenantId())  
139 - .setParameter("entity_id", entity.getEntityId())  
140 - .setParameter("key", entity.getKey())  
141 - .setParameter("ts", entity.getTs())  
142 - .setParameter("dbl_v", entity.getDoubleValue())  
143 - .executeUpdate();  
144 - }  
145 -  
146 - private static String getInsertOrUpdateString(String value, String nullValues) {  
147 - return "INSERT INTO tenant_ts_kv(tenant_id, entity_id, key, ts, " + value + ") VALUES (:tenant_id, :entity_id, :key, :ts, :" + value + ") ON CONFLICT (tenant_id, entity_id, key, ts) DO UPDATE SET " + value + " = :" + value + ", ts = :ts," + nullValues;  
148 - }  
149 } 89 }
@@ -15,11 +15,11 @@ @@ -15,11 +15,11 @@
15 */ 15 */
16 package org.thingsboard.server.dao.sqlts.timescale; 16 package org.thingsboard.server.dao.sqlts.timescale;
17 17
18 -import com.google.common.collect.Lists;  
19 import com.google.common.util.concurrent.Futures; 18 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture; 19 import com.google.common.util.concurrent.ListenableFuture;
21 import com.google.common.util.concurrent.SettableFuture; 20 import com.google.common.util.concurrent.SettableFuture;
22 import lombok.extern.slf4j.Slf4j; 21 import lombok.extern.slf4j.Slf4j;
  22 +import org.hibernate.exception.ConstraintViolationException;
23 import org.springframework.beans.factory.annotation.Autowired; 23 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.beans.factory.annotation.Value; 24 import org.springframework.beans.factory.annotation.Value;
25 import org.springframework.data.domain.PageRequest; 25 import org.springframework.data.domain.PageRequest;
@@ -29,19 +29,19 @@ import org.springframework.util.CollectionUtils; @@ -29,19 +29,19 @@ import org.springframework.util.CollectionUtils;
29 import org.thingsboard.server.common.data.id.EntityId; 29 import org.thingsboard.server.common.data.id.EntityId;
30 import org.thingsboard.server.common.data.id.TenantId; 30 import org.thingsboard.server.common.data.id.TenantId;
31 import org.thingsboard.server.common.data.kv.Aggregation; 31 import org.thingsboard.server.common.data.kv.Aggregation;
32 -import org.thingsboard.server.common.data.kv.BasicTsKvEntry;  
33 import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; 32 import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
34 import org.thingsboard.server.common.data.kv.ReadTsKvQuery; 33 import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
35 -import org.thingsboard.server.common.data.kv.StringDataEntry;  
36 import org.thingsboard.server.common.data.kv.TsKvEntry; 34 import org.thingsboard.server.common.data.kv.TsKvEntry;
37 -import org.thingsboard.server.common.data.kv.TsKvQuery;  
38 import org.thingsboard.server.dao.DaoUtil; 35 import org.thingsboard.server.dao.DaoUtil;
  36 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionary;
  37 +import org.thingsboard.server.dao.model.sqlts.dictionary.TsKvDictionaryCompositeKey;
39 import org.thingsboard.server.dao.model.sqlts.timescale.TimescaleTsKvEntity; 38 import org.thingsboard.server.dao.model.sqlts.timescale.TimescaleTsKvEntity;
40 -import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;  
41 import org.thingsboard.server.dao.sql.TbSqlBlockingQueue; 39 import org.thingsboard.server.dao.sql.TbSqlBlockingQueue;
42 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams; 40 import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;
43 import org.thingsboard.server.dao.sqlts.AbstractSqlTimeseriesDao; 41 import org.thingsboard.server.dao.sqlts.AbstractSqlTimeseriesDao;
44 -import org.thingsboard.server.dao.sqlts.AbstractTimeseriesInsertRepository; 42 +import org.thingsboard.server.dao.sqlts.EntityContainer;
  43 +import org.thingsboard.server.dao.sqlts.InsertTsRepository;
  44 +import org.thingsboard.server.dao.sqlts.dictionary.TsKvDictionaryRepository;
45 import org.thingsboard.server.dao.timeseries.TimeseriesDao; 45 import org.thingsboard.server.dao.timeseries.TimeseriesDao;
46 import org.thingsboard.server.dao.util.TimescaleDBTsDao; 46 import org.thingsboard.server.dao.util.TimescaleDBTsDao;
47 47
@@ -51,9 +51,11 @@ import java.util.ArrayList; @@ -51,9 +51,11 @@ import java.util.ArrayList;
51 import java.util.Collections; 51 import java.util.Collections;
52 import java.util.List; 52 import java.util.List;
53 import java.util.Optional; 53 import java.util.Optional;
  54 +import java.util.UUID;
54 import java.util.concurrent.CompletableFuture; 55 import java.util.concurrent.CompletableFuture;
55 -  
56 -import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; 56 +import java.util.concurrent.ConcurrentHashMap;
  57 +import java.util.concurrent.ConcurrentMap;
  58 +import java.util.concurrent.locks.ReentrantLock;
57 59
58 60
59 @Component 61 @Component
@@ -63,17 +65,21 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -63,17 +65,21 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
63 65
64 private static final String TS = "ts"; 66 private static final String TS = "ts";
65 67
  68 + private final ConcurrentMap<String, Integer> tsKvDictionaryMap = new ConcurrentHashMap<>();
  69 +
  70 + private static final ReentrantLock tsCreationLock = new ReentrantLock();
  71 +
66 @Autowired 72 @Autowired
67 - private TsKvTimescaleRepository tsKvRepository; 73 + private TsKvDictionaryRepository dictionaryRepository;
68 74
69 @Autowired 75 @Autowired
70 - private AggregationRepository aggregationRepository; 76 + private TsKvTimescaleRepository tsKvRepository;
71 77
72 @Autowired 78 @Autowired
73 - private AbstractTimeseriesInsertRepository insertRepository; 79 + private AggregationRepository aggregationRepository;
74 80
75 @Autowired 81 @Autowired
76 - ScheduledLogExecutorComponent logExecutor; 82 + private InsertTsRepository<TimescaleTsKvEntity> insertRepository;
77 83
78 @Value("${sql.ts_timescale.batch_size:1000}") 84 @Value("${sql.ts_timescale.batch_size:1000}")
79 private int batchSize; 85 private int batchSize;
@@ -84,10 +90,11 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -84,10 +90,11 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
84 @Value("${sql.ts_timescale.stats_print_interval_ms:1000}") 90 @Value("${sql.ts_timescale.stats_print_interval_ms:1000}")
85 private long statsPrintIntervalMs; 91 private long statsPrintIntervalMs;
86 92
87 - private TbSqlBlockingQueue<TimescaleTsKvEntity> queue; 93 + private TbSqlBlockingQueue<EntityContainer<TimescaleTsKvEntity>> queue;
88 94
89 @PostConstruct 95 @PostConstruct
90 - private void init() { 96 + protected void init() {
  97 + super.init();
91 TbSqlBlockingQueueParams params = TbSqlBlockingQueueParams.builder() 98 TbSqlBlockingQueueParams params = TbSqlBlockingQueueParams.builder()
92 .logName("TS Timescale") 99 .logName("TS Timescale")
93 .batchSize(batchSize) 100 .batchSize(batchSize)
@@ -99,17 +106,13 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -99,17 +106,13 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
99 } 106 }
100 107
101 @PreDestroy 108 @PreDestroy
102 - private void destroy() { 109 + protected void destroy() {
  110 + super.init();
103 if (queue != null) { 111 if (queue != null) {
104 queue.destroy(); 112 queue.destroy();
105 } 113 }
106 } 114 }
107 115
108 - @Override  
109 - public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {  
110 - return processFindAllAsync(tenantId, entityId, queries);  
111 - }  
112 -  
113 protected ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) { 116 protected ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
114 if (query.getAggregation() == Aggregation.NONE) { 117 if (query.getAggregation() == Aggregation.NONE) {
115 return findAllAsyncWithLimit(tenantId, entityId, query); 118 return findAllAsyncWithLimit(tenantId, entityId, query);
@@ -122,50 +125,36 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -122,50 +125,36 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
122 } 125 }
123 } 126 }
124 127
125 - private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {  
126 - return Futures.immediateFuture(  
127 - DaoUtil.convertDataList(  
128 - tsKvRepository.findAllWithLimit(  
129 - fromTimeUUID(tenantId.getId()),  
130 - fromTimeUUID(entityId.getId()),  
131 - query.getKey(),  
132 - query.getStartTs(),  
133 - query.getEndTs(),  
134 - new PageRequest(0, query.getLimit(),  
135 - new Sort(Sort.Direction.fromString(  
136 - query.getOrderBy()), "ts"))))); 128 + @Override
  129 + public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
  130 + return processFindAllAsync(tenantId, entityId, queries);
137 } 131 }
138 132
139 -  
140 @Override 133 @Override
141 public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) { 134 public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {
142 - ListenableFuture<List<TimescaleTsKvEntity>> future = getLatest(tenantId, entityId, key, 0L, System.currentTimeMillis());  
143 - return Futures.transform(future, latest -> {  
144 - if (!CollectionUtils.isEmpty(latest)) {  
145 - return DaoUtil.getData(latest.get(0));  
146 - } else {  
147 - return new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));  
148 - }  
149 - }, service); 135 + return getFindLatestFuture(entityId, key);
150 } 136 }
151 137
152 @Override 138 @Override
153 public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) { 139 public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {
154 - return Futures.immediateFuture(DaoUtil.convertDataList(Lists.newArrayList(tsKvRepository.findAllLatestValues(fromTimeUUID(tenantId.getId()), fromTimeUUID(entityId.getId()))))); 140 + return getFindAllLatestFuture(entityId);
155 } 141 }
156 142
157 @Override 143 @Override
158 public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) { 144 public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
  145 + String strKey = tsKvEntry.getKey();
  146 + Integer keyId = getOrSaveKeyId(strKey);
159 TimescaleTsKvEntity entity = new TimescaleTsKvEntity(); 147 TimescaleTsKvEntity entity = new TimescaleTsKvEntity();
160 - entity.setTenantId(fromTimeUUID(tenantId.getId()));  
161 - entity.setEntityId(fromTimeUUID(entityId.getId())); 148 + entity.setTenantId(tenantId.getId());
  149 + entity.setEntityId(entityId.getId());
162 entity.setTs(tsKvEntry.getTs()); 150 entity.setTs(tsKvEntry.getTs());
163 - entity.setKey(tsKvEntry.getKey()); 151 + entity.setKey(keyId);
164 entity.setStrValue(tsKvEntry.getStrValue().orElse(null)); 152 entity.setStrValue(tsKvEntry.getStrValue().orElse(null));
165 entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); 153 entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));
166 entity.setLongValue(tsKvEntry.getLongValue().orElse(null)); 154 entity.setLongValue(tsKvEntry.getLongValue().orElse(null));
167 entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); 155 entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));
168 - return queue.add(entity); 156 + log.trace("Saving entity to timescale db: {}", entity);
  157 + return queue.add(new EntityContainer(entity, null));
169 } 158 }
170 159
171 @Override 160 @Override
@@ -175,16 +164,18 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -175,16 +164,18 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
175 164
176 @Override 165 @Override
177 public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) { 166 public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
178 - return Futures.immediateFuture(null); 167 + return getSaveLatestFuture(entityId, tsKvEntry);
179 } 168 }
180 169
181 @Override 170 @Override
182 public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) { 171 public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
  172 + String strKey = query.getKey();
  173 + Integer keyId = getOrSaveKeyId(strKey);
183 return service.submit(() -> { 174 return service.submit(() -> {
184 tsKvRepository.delete( 175 tsKvRepository.delete(
185 - fromTimeUUID(tenantId.getId()),  
186 - fromTimeUUID(entityId.getId()),  
187 - query.getKey(), 176 + tenantId.getId(),
  177 + entityId.getId(),
  178 + keyId,
188 query.getStartTs(), 179 query.getStartTs(),
189 query.getEndTs()); 180 query.getEndTs());
190 return null; 181 return null;
@@ -193,7 +184,7 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -193,7 +184,7 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
193 184
194 @Override 185 @Override
195 public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) { 186 public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
196 - return service.submit(() -> null); 187 + return getRemoveLatestFuture(tenantId, entityId, query);
197 } 188 }
198 189
199 @Override 190 @Override
@@ -201,37 +192,60 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -201,37 +192,60 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
201 return service.submit(() -> null); 192 return service.submit(() -> null);
202 } 193 }
203 194
204 - private ListenableFuture<Void> getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
205 - ListenableFuture<List<TsKvEntry>> future = findNewLatestEntryFuture(tenantId, entityId, query);  
206 - return Futures.transformAsync(future, entryList -> {  
207 - if (entryList.size() == 1) {  
208 - return save(tenantId, entityId, entryList.get(0), 0L);  
209 - } else {  
210 - log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());  
211 - }  
212 - return Futures.immediateFuture(null);  
213 - }, service);  
214 - }  
215 -  
216 - private ListenableFuture<List<TimescaleTsKvEntity>> findLatestByQuery(TenantId tenantId, EntityId entityId, TsKvQuery query) {  
217 - return getLatest(tenantId, entityId, query.getKey(), query.getStartTs(), query.getEndTs()); 195 + private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
  196 + String strKey = query.getKey();
  197 + Integer keyId = getOrSaveKeyId(strKey);
  198 + List<TimescaleTsKvEntity> timescaleTsKvEntities = tsKvRepository.findAllWithLimit(
  199 + tenantId.getId(),
  200 + entityId.getId(),
  201 + keyId,
  202 + query.getStartTs(),
  203 + query.getEndTs(),
  204 + new PageRequest(0, query.getLimit(),
  205 + new Sort(Sort.Direction.fromString(
  206 + query.getOrderBy()), TS)));
  207 + timescaleTsKvEntities.forEach(tsKvEntity -> tsKvEntity.setStrKey(strKey));
  208 + return Futures.immediateFuture(DaoUtil.convertDataList(timescaleTsKvEntities));
218 } 209 }
219 210
220 - private ListenableFuture<List<TimescaleTsKvEntity>> getLatest(TenantId tenantId, EntityId entityId, String key, long start, long end) {  
221 - return Futures.immediateFuture(tsKvRepository.findAllWithLimit(  
222 - fromTimeUUID(tenantId.getId()),  
223 - fromTimeUUID(entityId.getId()),  
224 - key,  
225 - start,  
226 - end,  
227 - new PageRequest(0, 1,  
228 - new Sort(Sort.Direction.DESC, TS)))); 211 + private Integer getOrSaveKeyId(String strKey) {
  212 + Integer keyId = tsKvDictionaryMap.get(strKey);
  213 + if (keyId == null) {
  214 + Optional<TsKvDictionary> tsKvDictionaryOptional;
  215 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  216 + if (!tsKvDictionaryOptional.isPresent()) {
  217 + tsCreationLock.lock();
  218 + try {
  219 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  220 + if (!tsKvDictionaryOptional.isPresent()) {
  221 + TsKvDictionary tsKvDictionary = new TsKvDictionary();
  222 + tsKvDictionary.setKey(strKey);
  223 + try {
  224 + TsKvDictionary saved = dictionaryRepository.save(tsKvDictionary);
  225 + tsKvDictionaryMap.put(saved.getKey(), saved.getKeyId());
  226 + keyId = saved.getKeyId();
  227 + } catch (ConstraintViolationException e) {
  228 + tsKvDictionaryOptional = dictionaryRepository.findById(new TsKvDictionaryCompositeKey(strKey));
  229 + TsKvDictionary dictionary = tsKvDictionaryOptional.orElseThrow(() -> new RuntimeException("Failed to get TsKvDictionary entity from DB!"));
  230 + tsKvDictionaryMap.put(dictionary.getKey(), dictionary.getKeyId());
  231 + keyId = dictionary.getKeyId();
  232 + }
  233 + } else {
  234 + keyId = tsKvDictionaryOptional.get().getKeyId();
  235 + }
  236 + } finally {
  237 + tsCreationLock.unlock();
  238 + }
  239 + } else {
  240 + keyId = tsKvDictionaryOptional.get().getKeyId();
  241 + tsKvDictionaryMap.put(strKey, keyId);
  242 + }
  243 + }
  244 + return keyId;
229 } 245 }
230 246
231 private ListenableFuture<List<Optional<TsKvEntry>>> findAndAggregateAsync(TenantId tenantId, EntityId entityId, String key, long startTs, long endTs, long timeBucket, Aggregation aggregation) { 247 private ListenableFuture<List<Optional<TsKvEntry>>> findAndAggregateAsync(TenantId tenantId, EntityId entityId, String key, long startTs, long endTs, long timeBucket, Aggregation aggregation) {
232 - String entityIdStr = fromTimeUUID(entityId.getId());  
233 - String tenantIdStr = fromTimeUUID(tenantId.getId());  
234 - CompletableFuture<List<TimescaleTsKvEntity>> listCompletableFuture = switchAgregation(key, startTs, endTs, timeBucket, aggregation, entityIdStr, tenantIdStr); 248 + CompletableFuture<List<TimescaleTsKvEntity>> listCompletableFuture = switchAgregation(key, startTs, endTs, timeBucket, aggregation, entityId.getId(), tenantId.getId());
235 SettableFuture<List<TimescaleTsKvEntity>> listenableFuture = SettableFuture.create(); 249 SettableFuture<List<TimescaleTsKvEntity>> listenableFuture = SettableFuture.create();
236 listCompletableFuture.whenComplete((timescaleTsKvEntities, throwable) -> { 250 listCompletableFuture.whenComplete((timescaleTsKvEntities, throwable) -> {
237 if (throwable != null) { 251 if (throwable != null) {
@@ -245,9 +259,9 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -245,9 +259,9 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
245 List<Optional<TsKvEntry>> result = new ArrayList<>(); 259 List<Optional<TsKvEntry>> result = new ArrayList<>();
246 timescaleTsKvEntities.forEach(entity -> { 260 timescaleTsKvEntities.forEach(entity -> {
247 if (entity != null && entity.isNotEmpty()) { 261 if (entity != null && entity.isNotEmpty()) {
248 - entity.setEntityId(entityIdStr);  
249 - entity.setTenantId(tenantIdStr);  
250 - entity.setKey(key); 262 + entity.setEntityId(entityId.getId());
  263 + entity.setTenantId(tenantId.getId());
  264 + entity.setStrKey(key);
251 result.add(Optional.of(DaoUtil.getData(entity))); 265 result.add(Optional.of(DaoUtil.getData(entity)));
252 } else { 266 } else {
253 result.add(Optional.empty()); 267 result.add(Optional.empty());
@@ -260,69 +274,74 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements @@ -260,69 +274,74 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
260 }); 274 });
261 } 275 }
262 276
263 - private CompletableFuture<List<TimescaleTsKvEntity>> switchAgregation(String key, long startTs, long endTs, long timeBucket, Aggregation aggregation, String entityIdStr, String tenantIdStr) { 277 + private CompletableFuture<List<TimescaleTsKvEntity>> switchAgregation(String key, long startTs, long endTs, long timeBucket, Aggregation aggregation, UUID entityId, UUID tenantId) {
264 switch (aggregation) { 278 switch (aggregation) {
265 case AVG: 279 case AVG:
266 - return findAvg(key, startTs, endTs, timeBucket, entityIdStr, tenantIdStr); 280 + return findAvg(key, startTs, endTs, timeBucket, entityId, tenantId);
267 case MAX: 281 case MAX:
268 - return findMax(key, startTs, endTs, timeBucket, entityIdStr, tenantIdStr); 282 + return findMax(key, startTs, endTs, timeBucket, entityId, tenantId);
269 case MIN: 283 case MIN:
270 - return findMin(key, startTs, endTs, timeBucket, entityIdStr, tenantIdStr); 284 + return findMin(key, startTs, endTs, timeBucket, entityId, tenantId);
271 case SUM: 285 case SUM:
272 - return findSum(key, startTs, endTs, timeBucket, entityIdStr, tenantIdStr); 286 + return findSum(key, startTs, endTs, timeBucket, entityId, tenantId);
273 case COUNT: 287 case COUNT:
274 - return findCount(key, startTs, endTs, timeBucket, entityIdStr, tenantIdStr); 288 + return findCount(key, startTs, endTs, timeBucket, entityId, tenantId);
275 default: 289 default:
276 throw new IllegalArgumentException("Not supported aggregation type: " + aggregation); 290 throw new IllegalArgumentException("Not supported aggregation type: " + aggregation);
277 } 291 }
278 } 292 }
279 293
280 - private CompletableFuture<List<TimescaleTsKvEntity>> findAvg(String key, long startTs, long endTs, long timeBucket, String entityIdStr, String tenantIdStr) { 294 + private CompletableFuture<List<TimescaleTsKvEntity>> findAvg(String key, long startTs, long endTs, long timeBucket, UUID entityId, UUID tenantId) {
  295 + Integer keyId = getOrSaveKeyId(key);
281 return aggregationRepository.findAvg( 296 return aggregationRepository.findAvg(
282 - tenantIdStr,  
283 - entityIdStr,  
284 - key, 297 + tenantId,
  298 + entityId,
  299 + keyId,
285 timeBucket, 300 timeBucket,
286 startTs, 301 startTs,
287 endTs); 302 endTs);
288 } 303 }
289 304
290 - private CompletableFuture<List<TimescaleTsKvEntity>> findMax(String key, long startTs, long endTs, long timeBucket, String entityIdStr, String tenantIdStr) { 305 + private CompletableFuture<List<TimescaleTsKvEntity>> findMax(String key, long startTs, long endTs, long timeBucket, UUID entityId, UUID tenantId) {
  306 + Integer keyId = getOrSaveKeyId(key);
291 return aggregationRepository.findMax( 307 return aggregationRepository.findMax(
292 - tenantIdStr,  
293 - entityIdStr,  
294 - key, 308 + tenantId,
  309 + entityId,
  310 + keyId,
295 timeBucket, 311 timeBucket,
296 startTs, 312 startTs,
297 endTs); 313 endTs);
298 } 314 }
299 315
300 - private CompletableFuture<List<TimescaleTsKvEntity>> findMin(String key, long startTs, long endTs, long timeBucket, String entityIdStr, String tenantIdStr) { 316 + private CompletableFuture<List<TimescaleTsKvEntity>> findMin(String key, long startTs, long endTs, long timeBucket, UUID entityId, UUID tenantId) {
  317 + Integer keyId = getOrSaveKeyId(key);
301 return aggregationRepository.findMin( 318 return aggregationRepository.findMin(
302 - tenantIdStr,  
303 - entityIdStr,  
304 - key, 319 + tenantId,
  320 + entityId,
  321 + keyId,
305 timeBucket, 322 timeBucket,
306 startTs, 323 startTs,
307 endTs); 324 endTs);
308 325
309 } 326 }
310 327
311 - private CompletableFuture<List<TimescaleTsKvEntity>> findSum(String key, long startTs, long endTs, long timeBucket, String entityIdStr, String tenantIdStr) { 328 + private CompletableFuture<List<TimescaleTsKvEntity>> findSum(String key, long startTs, long endTs, long timeBucket, UUID entityId, UUID tenantId) {
  329 + Integer keyId = getOrSaveKeyId(key);
312 return aggregationRepository.findSum( 330 return aggregationRepository.findSum(
313 - tenantIdStr,  
314 - entityIdStr,  
315 - key, 331 + tenantId,
  332 + entityId,
  333 + keyId,
316 timeBucket, 334 timeBucket,
317 startTs, 335 startTs,
318 endTs); 336 endTs);
319 } 337 }
320 338
321 - private CompletableFuture<List<TimescaleTsKvEntity>> findCount(String key, long startTs, long endTs, long timeBucket, String entityIdStr, String tenantIdStr) { 339 + private CompletableFuture<List<TimescaleTsKvEntity>> findCount(String key, long startTs, long endTs, long timeBucket, UUID entityId, UUID tenantId) {
  340 + Integer keyId = getOrSaveKeyId(key);
322 return aggregationRepository.findCount( 341 return aggregationRepository.findCount(
323 - tenantIdStr,  
324 - entityIdStr,  
325 - key, 342 + tenantId,
  343 + entityId,
  344 + keyId,
326 timeBucket, 345 timeBucket,
327 startTs, 346 startTs,
328 endTs); 347 endTs);
@@ -26,6 +26,7 @@ import org.thingsboard.server.dao.model.sqlts.timescale.TimescaleTsKvEntity; @@ -26,6 +26,7 @@ import org.thingsboard.server.dao.model.sqlts.timescale.TimescaleTsKvEntity;
26 import org.thingsboard.server.dao.util.TimescaleDBTsDao; 26 import org.thingsboard.server.dao.util.TimescaleDBTsDao;
27 27
28 import java.util.List; 28 import java.util.List;
  29 +import java.util.UUID;
29 30
30 @TimescaleDBTsDao 31 @TimescaleDBTsDao
31 public interface TsKvTimescaleRepository extends CrudRepository<TimescaleTsKvEntity, TimescaleTsKvCompositeKey> { 32 public interface TsKvTimescaleRepository extends CrudRepository<TimescaleTsKvEntity, TimescaleTsKvCompositeKey> {
@@ -35,31 +36,21 @@ public interface TsKvTimescaleRepository extends CrudRepository<TimescaleTsKvEnt @@ -35,31 +36,21 @@ public interface TsKvTimescaleRepository extends CrudRepository<TimescaleTsKvEnt
35 "AND tskv.key = :entityKey " + 36 "AND tskv.key = :entityKey " +
36 "AND tskv.ts > :startTs AND tskv.ts <= :endTs") 37 "AND tskv.ts > :startTs AND tskv.ts <= :endTs")
37 List<TimescaleTsKvEntity> findAllWithLimit( 38 List<TimescaleTsKvEntity> findAllWithLimit(
38 - @Param("tenantId") String tenantId,  
39 - @Param("entityId") String entityId,  
40 - @Param("entityKey") String key, 39 + @Param("tenantId") UUID tenantId,
  40 + @Param("entityId") UUID entityId,
  41 + @Param("entityKey") int key,
41 @Param("startTs") long startTs, 42 @Param("startTs") long startTs,
42 @Param("endTs") long endTs, Pageable pageable); 43 @Param("endTs") long endTs, Pageable pageable);
43 44
44 - @Query(value = "SELECT tskv.tenant_id as tenant_id, tskv.entity_id as entity_id, tskv.key as key, last(tskv.ts,tskv.ts) as ts," +  
45 - " last(tskv.bool_v, tskv.ts) as bool_v, last(tskv.str_v, tskv.ts) as str_v," +  
46 - " last(tskv.long_v, tskv.ts) as long_v, last(tskv.dbl_v, tskv.ts) as dbl_v" +  
47 - " FROM tenant_ts_kv tskv WHERE tskv.tenant_id = cast(:tenantId AS varchar) " +  
48 - "AND tskv.entity_id = cast(:entityId AS varchar) " +  
49 - "GROUP BY tskv.tenant_id, tskv.entity_id, tskv.key", nativeQuery = true)  
50 - List<TimescaleTsKvEntity> findAllLatestValues(  
51 - @Param("tenantId") String tenantId,  
52 - @Param("entityId") String entityId);  
53 -  
54 @Transactional 45 @Transactional
55 @Modifying 46 @Modifying
56 @Query("DELETE FROM TimescaleTsKvEntity tskv WHERE tskv.tenantId = :tenantId " + 47 @Query("DELETE FROM TimescaleTsKvEntity tskv WHERE tskv.tenantId = :tenantId " +
57 "AND tskv.entityId = :entityId " + 48 "AND tskv.entityId = :entityId " +
58 "AND tskv.key = :entityKey " + 49 "AND tskv.key = :entityKey " +
59 "AND tskv.ts > :startTs AND tskv.ts <= :endTs") 50 "AND tskv.ts > :startTs AND tskv.ts <= :endTs")
60 - void delete(@Param("tenantId") String tenantId,  
61 - @Param("entityId") String entityId,  
62 - @Param("entityKey") String key, 51 + void delete(@Param("tenantId") UUID tenantId,
  52 + @Param("entityId") UUID entityId,
  53 + @Param("entityKey") int key,
63 @Param("startTs") long startTs, 54 @Param("startTs") long startTs,
64 @Param("endTs") long endTs); 55 @Param("endTs") long endTs);
65 56
1 -/**  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.dao.sqlts.ts;  
17 -  
18 -import com.google.common.collect.Lists;  
19 -import com.google.common.util.concurrent.FutureCallback;  
20 -import com.google.common.util.concurrent.Futures;  
21 -import com.google.common.util.concurrent.ListenableFuture;  
22 -import com.google.common.util.concurrent.SettableFuture;  
23 -import lombok.extern.slf4j.Slf4j;  
24 -import org.springframework.beans.factory.annotation.Autowired;  
25 -import org.springframework.beans.factory.annotation.Value;  
26 -import org.springframework.data.domain.PageRequest;  
27 -import org.springframework.data.domain.Sort;  
28 -import org.springframework.stereotype.Component;  
29 -import org.thingsboard.server.common.data.UUIDConverter;  
30 -import org.thingsboard.server.common.data.id.EntityId;  
31 -import org.thingsboard.server.common.data.id.TenantId;  
32 -import org.thingsboard.server.common.data.kv.Aggregation;  
33 -import org.thingsboard.server.common.data.kv.BasicTsKvEntry;  
34 -import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;  
35 -import org.thingsboard.server.common.data.kv.ReadTsKvQuery;  
36 -import org.thingsboard.server.common.data.kv.StringDataEntry;  
37 -import org.thingsboard.server.common.data.kv.TsKvEntry;  
38 -import org.thingsboard.server.dao.DaoUtil;  
39 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity;  
40 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvLatestCompositeKey;  
41 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvLatestEntity;  
42 -import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;  
43 -import org.thingsboard.server.dao.sql.TbSqlBlockingQueue;  
44 -import org.thingsboard.server.dao.sql.TbSqlBlockingQueueParams;  
45 -import org.thingsboard.server.dao.sqlts.AbstractLatestInsertRepository;  
46 -import org.thingsboard.server.dao.sqlts.AbstractSqlTimeseriesDao;  
47 -import org.thingsboard.server.dao.sqlts.AbstractTimeseriesInsertRepository;  
48 -import org.thingsboard.server.dao.timeseries.SimpleListenableFuture;  
49 -import org.thingsboard.server.dao.timeseries.TimeseriesDao;  
50 -import org.thingsboard.server.dao.util.SqlTsDao;  
51 -  
52 -import javax.annotation.Nullable;  
53 -import javax.annotation.PostConstruct;  
54 -import javax.annotation.PreDestroy;  
55 -import java.util.ArrayList;  
56 -import java.util.List;  
57 -import java.util.Optional;  
58 -import java.util.concurrent.CompletableFuture;  
59 -import java.util.concurrent.ExecutionException;  
60 -import java.util.stream.Collectors;  
61 -  
62 -import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID;  
63 -  
64 -  
65 -@Component  
66 -@Slf4j  
67 -@SqlTsDao  
68 -public class JpaTimeseriesDao extends AbstractSqlTimeseriesDao implements TimeseriesDao {  
69 -  
70 - @Autowired  
71 - private TsKvRepository tsKvRepository;  
72 -  
73 - @Autowired  
74 - private TsKvLatestRepository tsKvLatestRepository;  
75 -  
76 - @Autowired  
77 - private AbstractTimeseriesInsertRepository insertRepository;  
78 -  
79 - @Autowired  
80 - private AbstractLatestInsertRepository insertLatestRepository;  
81 -  
82 - @Autowired  
83 - ScheduledLogExecutorComponent logExecutor;  
84 -  
85 - @Value("${sql.ts.batch_size:1000}")  
86 - private int tsBatchSize;  
87 -  
88 - @Value("${sql.ts.batch_max_delay:100}")  
89 - private long tsMaxDelay;  
90 -  
91 - @Value("${sql.ts.stats_print_interval_ms:1000}")  
92 - private long tsStatsPrintIntervalMs;  
93 -  
94 - @Value("${sql.ts_latest.batch_size:1000}")  
95 - private int tsLatestBatchSize;  
96 -  
97 - @Value("${sql.ts_latest.batch_max_delay:100}")  
98 - private long tsLatestMaxDelay;  
99 -  
100 - @Value("${sql.ts_latest.stats_print_interval_ms:1000}")  
101 - private long tsLatestStatsPrintIntervalMs;  
102 -  
103 - private TbSqlBlockingQueue<TsKvEntity> tsQueue;  
104 - private TbSqlBlockingQueue<TsKvLatestEntity> tsLatestQueue;  
105 -  
106 -  
107 - @PostConstruct  
108 - private void init() {  
109 - TbSqlBlockingQueueParams tsParams = TbSqlBlockingQueueParams.builder()  
110 - .logName("TS")  
111 - .batchSize(tsBatchSize)  
112 - .maxDelay(tsMaxDelay)  
113 - .statsPrintIntervalMs(tsStatsPrintIntervalMs)  
114 - .build();  
115 - tsQueue = new TbSqlBlockingQueue<>(tsParams);  
116 - tsQueue.init(logExecutor, v -> insertRepository.saveOrUpdate(v));  
117 -  
118 - TbSqlBlockingQueueParams tsLatestParams = TbSqlBlockingQueueParams.builder()  
119 - .logName("TS Latest")  
120 - .batchSize(tsLatestBatchSize)  
121 - .maxDelay(tsLatestMaxDelay)  
122 - .statsPrintIntervalMs(tsLatestStatsPrintIntervalMs)  
123 - .build();  
124 - tsLatestQueue = new TbSqlBlockingQueue<>(tsLatestParams);  
125 - tsLatestQueue.init(logExecutor, v -> insertLatestRepository.saveOrUpdate(v));  
126 - }  
127 -  
128 - @PreDestroy  
129 - private void destroy() {  
130 - if (tsQueue != null) {  
131 - tsQueue.destroy();  
132 - }  
133 -  
134 - if (tsLatestQueue != null) {  
135 - tsLatestQueue.destroy();  
136 - }  
137 - }  
138 -  
139 - @Override  
140 - public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {  
141 - return processFindAllAsync(tenantId, entityId, queries);  
142 - }  
143 -  
144 - protected ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {  
145 - if (query.getAggregation() == Aggregation.NONE) {  
146 - return findAllAsyncWithLimit(entityId, query);  
147 - } else {  
148 - long stepTs = query.getStartTs();  
149 - List<ListenableFuture<Optional<TsKvEntry>>> futures = new ArrayList<>();  
150 - while (stepTs < query.getEndTs()) {  
151 - long startTs = stepTs;  
152 - long endTs = stepTs + query.getInterval();  
153 - long ts = startTs + (endTs - startTs) / 2;  
154 - futures.add(findAndAggregateAsync(tenantId, entityId, query.getKey(), startTs, endTs, ts, query.getAggregation()));  
155 - stepTs = endTs;  
156 - }  
157 - return getTskvEntriesFuture(Futures.allAsList(futures));  
158 - }  
159 - }  
160 -  
161 - private ListenableFuture<Optional<TsKvEntry>> findAndAggregateAsync(TenantId tenantId, EntityId entityId, String key, long startTs, long endTs, long ts, Aggregation aggregation) {  
162 - List<CompletableFuture<TsKvEntity>> entitiesFutures = new ArrayList<>();  
163 - String entityIdStr = fromTimeUUID(entityId.getId());  
164 - switchAgregation(entityId, key, startTs, endTs, aggregation, entitiesFutures, entityIdStr);  
165 -  
166 - SettableFuture<TsKvEntity> listenableFuture = SettableFuture.create();  
167 -  
168 - CompletableFuture<List<TsKvEntity>> entities =  
169 - CompletableFuture.allOf(entitiesFutures.toArray(new CompletableFuture[entitiesFutures.size()]))  
170 - .thenApply(v -> entitiesFutures.stream()  
171 - .map(CompletableFuture::join)  
172 - .collect(Collectors.toList()));  
173 -  
174 - entities.whenComplete((tsKvEntities, throwable) -> {  
175 - if (throwable != null) {  
176 - listenableFuture.setException(throwable);  
177 - } else {  
178 - TsKvEntity result = null;  
179 - for (TsKvEntity entity : tsKvEntities) {  
180 - if (entity.isNotEmpty()) {  
181 - result = entity;  
182 - break;  
183 - }  
184 - }  
185 - listenableFuture.set(result);  
186 - }  
187 - });  
188 - return Futures.transform(listenableFuture, entity -> {  
189 - if (entity != null && entity.isNotEmpty()) {  
190 - entity.setEntityId(entityIdStr);  
191 - entity.setEntityType(entityId.getEntityType());  
192 - entity.setKey(key);  
193 - entity.setTs(ts);  
194 - return Optional.of(DaoUtil.getData(entity));  
195 - } else {  
196 - return Optional.empty();  
197 - }  
198 - });  
199 - }  
200 -  
201 - private void switchAgregation(EntityId entityId, String key, long startTs, long endTs, Aggregation aggregation, List<CompletableFuture<TsKvEntity>> entitiesFutures, String entityIdStr) {  
202 - switch (aggregation) {  
203 - case AVG:  
204 - findAvg(entityId, key, startTs, endTs, entitiesFutures, entityIdStr);  
205 - break;  
206 - case MAX:  
207 - findMax(entityId, key, startTs, endTs, entitiesFutures, entityIdStr);  
208 - break;  
209 - case MIN:  
210 - findMin(entityId, key, startTs, endTs, entitiesFutures, entityIdStr);  
211 - break;  
212 - case SUM:  
213 - findSum(entityId, key, startTs, endTs, entitiesFutures, entityIdStr);  
214 - break;  
215 - case COUNT:  
216 - findCount(entityId, key, startTs, endTs, entitiesFutures, entityIdStr);  
217 - break;  
218 - default:  
219 - throw new IllegalArgumentException("Not supported aggregation type: " + aggregation);  
220 - }  
221 - }  
222 -  
223 - private void findCount(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures, String entityIdStr) {  
224 - entitiesFutures.add(tsKvRepository.findCount(  
225 - entityIdStr,  
226 - entityId.getEntityType(),  
227 - key,  
228 - startTs,  
229 - endTs));  
230 - }  
231 -  
232 - private void findSum(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures, String entityIdStr) {  
233 - entitiesFutures.add(tsKvRepository.findSum(  
234 - entityIdStr,  
235 - entityId.getEntityType(),  
236 - key,  
237 - startTs,  
238 - endTs));  
239 - }  
240 -  
241 - private void findMin(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures, String entityIdStr) {  
242 - entitiesFutures.add(tsKvRepository.findStringMin(  
243 - entityIdStr,  
244 - entityId.getEntityType(),  
245 - key,  
246 - startTs,  
247 - endTs));  
248 - entitiesFutures.add(tsKvRepository.findNumericMin(  
249 - entityIdStr,  
250 - entityId.getEntityType(),  
251 - key,  
252 - startTs,  
253 - endTs));  
254 - }  
255 -  
256 - private void findMax(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures, String entityIdStr) {  
257 - entitiesFutures.add(tsKvRepository.findStringMax(  
258 - entityIdStr,  
259 - entityId.getEntityType(),  
260 - key,  
261 - startTs,  
262 - endTs));  
263 - entitiesFutures.add(tsKvRepository.findNumericMax(  
264 - entityIdStr,  
265 - entityId.getEntityType(),  
266 - key,  
267 - startTs,  
268 - endTs));  
269 - }  
270 -  
271 - private void findAvg(EntityId entityId, String key, long startTs, long endTs, List<CompletableFuture<TsKvEntity>> entitiesFutures, String entityIdStr) {  
272 - entitiesFutures.add(tsKvRepository.findAvg(  
273 - entityIdStr,  
274 - entityId.getEntityType(),  
275 - key,  
276 - startTs,  
277 - endTs));  
278 - }  
279 -  
280 - private ListenableFuture<List<TsKvEntry>> findAllAsyncWithLimit(EntityId entityId, ReadTsKvQuery query) {  
281 - return Futures.immediateFuture(  
282 - DaoUtil.convertDataList(  
283 - tsKvRepository.findAllWithLimit(  
284 - fromTimeUUID(entityId.getId()),  
285 - entityId.getEntityType(),  
286 - query.getKey(),  
287 - query.getStartTs(),  
288 - query.getEndTs(),  
289 - new PageRequest(0, query.getLimit(),  
290 - new Sort(Sort.Direction.fromString(  
291 - query.getOrderBy()), "ts")))));  
292 - }  
293 -  
294 - @Override  
295 - public ListenableFuture<TsKvEntry> findLatest(TenantId tenantId, EntityId entityId, String key) {  
296 - TsKvLatestCompositeKey compositeKey =  
297 - new TsKvLatestCompositeKey(  
298 - entityId.getEntityType(),  
299 - fromTimeUUID(entityId.getId()),  
300 - key);  
301 - Optional<TsKvLatestEntity> entry = tsKvLatestRepository.findById(compositeKey);  
302 - TsKvEntry result;  
303 - if (entry.isPresent()) {  
304 - result = DaoUtil.getData(entry.get());  
305 - } else {  
306 - result = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(key, null));  
307 - }  
308 - return Futures.immediateFuture(result);  
309 - }  
310 -  
311 - @Override  
312 - public ListenableFuture<List<TsKvEntry>> findAllLatest(TenantId tenantId, EntityId entityId) {  
313 - return Futures.immediateFuture(  
314 - DaoUtil.convertDataList(Lists.newArrayList(  
315 - tsKvLatestRepository.findAllByEntityTypeAndEntityId(  
316 - entityId.getEntityType(),  
317 - UUIDConverter.fromTimeUUID(entityId.getId())))));  
318 - }  
319 -  
320 - @Override  
321 - public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {  
322 - TsKvEntity entity = new TsKvEntity();  
323 - entity.setEntityType(entityId.getEntityType());  
324 - entity.setEntityId(fromTimeUUID(entityId.getId()));  
325 - entity.setTs(tsKvEntry.getTs());  
326 - entity.setKey(tsKvEntry.getKey());  
327 - entity.setStrValue(tsKvEntry.getStrValue().orElse(null));  
328 - entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));  
329 - entity.setLongValue(tsKvEntry.getLongValue().orElse(null));  
330 - entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));  
331 - log.trace("Saving entity: {}", entity);  
332 - return tsQueue.add(entity);  
333 - }  
334 -  
335 - @Override  
336 - public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {  
337 - return Futures.immediateFuture(null);  
338 - }  
339 -  
340 - @Override  
341 - public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {  
342 - TsKvLatestEntity latestEntity = new TsKvLatestEntity();  
343 - latestEntity.setEntityType(entityId.getEntityType());  
344 - latestEntity.setEntityId(fromTimeUUID(entityId.getId()));  
345 - latestEntity.setTs(tsKvEntry.getTs());  
346 - latestEntity.setKey(tsKvEntry.getKey());  
347 - latestEntity.setStrValue(tsKvEntry.getStrValue().orElse(null));  
348 - latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null));  
349 - latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null));  
350 - latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null));  
351 - return tsLatestQueue.add(latestEntity);  
352 - }  
353 -  
354 - @Override  
355 - public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
356 - return service.submit(() -> {  
357 - tsKvRepository.delete(  
358 - fromTimeUUID(entityId.getId()),  
359 - entityId.getEntityType(),  
360 - query.getKey(),  
361 - query.getStartTs(),  
362 - query.getEndTs());  
363 - return null;  
364 - });  
365 - }  
366 -  
367 - @Override  
368 - public ListenableFuture<Void> removeLatest(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
369 - ListenableFuture<TsKvEntry> latestFuture = findLatest(tenantId, entityId, query.getKey());  
370 -  
371 - ListenableFuture<Boolean> booleanFuture = Futures.transform(latestFuture, tsKvEntry -> {  
372 - long ts = tsKvEntry.getTs();  
373 - return ts > query.getStartTs() && ts <= query.getEndTs();  
374 - }, service);  
375 -  
376 - ListenableFuture<Void> removedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {  
377 - if (isRemove) {  
378 - TsKvLatestEntity latestEntity = new TsKvLatestEntity();  
379 - latestEntity.setEntityType(entityId.getEntityType());  
380 - latestEntity.setEntityId(fromTimeUUID(entityId.getId()));  
381 - latestEntity.setKey(query.getKey());  
382 - return service.submit(() -> {  
383 - tsKvLatestRepository.delete(latestEntity);  
384 - return null;  
385 - });  
386 - }  
387 - return Futures.immediateFuture(null);  
388 - }, service);  
389 -  
390 - final SimpleListenableFuture<Void> resultFuture = new SimpleListenableFuture<>();  
391 - Futures.addCallback(removedLatestFuture, new FutureCallback<Void>() {  
392 - @Override  
393 - public void onSuccess(@Nullable Void result) {  
394 - if (query.getRewriteLatestIfDeleted()) {  
395 - ListenableFuture<Void> savedLatestFuture = Futures.transformAsync(booleanFuture, isRemove -> {  
396 - if (isRemove) {  
397 - return getNewLatestEntryFuture(tenantId, entityId, query);  
398 - }  
399 - return Futures.immediateFuture(null);  
400 - }, service);  
401 -  
402 - try {  
403 - resultFuture.set(savedLatestFuture.get());  
404 - } catch (InterruptedException | ExecutionException e) {  
405 - log.warn("Could not get latest saved value for [{}], {}", entityId, query.getKey(), e);  
406 - }  
407 - } else {  
408 - resultFuture.set(null);  
409 - }  
410 - }  
411 -  
412 - @Override  
413 - public void onFailure(Throwable t) {  
414 - log.warn("[{}] Failed to process remove of the latest value", entityId, t);  
415 - }  
416 - });  
417 - return resultFuture;  
418 - }  
419 -  
420 - private ListenableFuture<Void> getNewLatestEntryFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
421 - ListenableFuture<List<TsKvEntry>> future = findNewLatestEntryFuture(tenantId, entityId, query);  
422 - return Futures.transformAsync(future, entryList -> {  
423 - if (entryList.size() == 1) {  
424 - return saveLatest(tenantId, entityId, entryList.get(0));  
425 - } else {  
426 - log.trace("Could not find new latest value for [{}], key - {}", entityId, query.getKey());  
427 - }  
428 - return Futures.immediateFuture(null);  
429 - }, service);  
430 - }  
431 -  
432 - @Override  
433 - public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {  
434 - return service.submit(() -> null);  
435 - }  
436 -}  
1 -/**  
2 - * Copyright © 2016-2020 The Thingsboard Authors  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -package org.thingsboard.server.dao.sqlts.ts;  
17 -  
18 -import org.springframework.jdbc.core.BatchPreparedStatementSetter;  
19 -import org.springframework.stereotype.Repository;  
20 -import org.springframework.transaction.annotation.Transactional;  
21 -import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity;  
22 -import org.thingsboard.server.dao.sqlts.AbstractTimeseriesInsertRepository;  
23 -import org.thingsboard.server.dao.util.PsqlDao;  
24 -import org.thingsboard.server.dao.util.SqlTsDao;  
25 -  
26 -import java.sql.PreparedStatement;  
27 -import java.sql.SQLException;  
28 -import java.sql.Types;  
29 -import java.util.List;  
30 -  
31 -@SqlTsDao  
32 -@PsqlDao  
33 -@Repository  
34 -@Transactional  
35 -public class PsqlTimeseriesInsertRepository extends AbstractTimeseriesInsertRepository<TsKvEntity> {  
36 -  
37 - private static final String TS_KV_CONSTRAINT = "(entity_type, entity_id, key, ts)";  
38 -  
39 - private static final String INSERT_OR_UPDATE_BOOL_STATEMENT = getInsertOrUpdateStringPsql(TS_KV_TABLE, TS_KV_CONSTRAINT, BOOL_V, PSQL_ON_BOOL_VALUE_UPDATE_SET_NULLS);  
40 - private static final String INSERT_OR_UPDATE_STR_STATEMENT = getInsertOrUpdateStringPsql(TS_KV_TABLE, TS_KV_CONSTRAINT, STR_V, PSQL_ON_STR_VALUE_UPDATE_SET_NULLS);  
41 - private static final String INSERT_OR_UPDATE_LONG_STATEMENT = getInsertOrUpdateStringPsql(TS_KV_TABLE, TS_KV_CONSTRAINT, LONG_V, PSQL_ON_LONG_VALUE_UPDATE_SET_NULLS);  
42 - private static final String INSERT_OR_UPDATE_DBL_STATEMENT = getInsertOrUpdateStringPsql(TS_KV_TABLE, TS_KV_CONSTRAINT, DBL_V, PSQL_ON_DBL_VALUE_UPDATE_SET_NULLS);  
43 -  
44 - private static final String INSERT_OR_UPDATE =  
45 - "INSERT INTO ts_kv (entity_type, entity_id, key, ts, bool_v, str_v, long_v, dbl_v) VALUES(?, ?, ?, ?, ?, ?, ?, ?) " +  
46 - "ON CONFLICT (entity_type, entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;";  
47 -  
48 - @Override  
49 - public void saveOrUpdate(TsKvEntity entity) {  
50 - processSaveOrUpdate(entity, INSERT_OR_UPDATE_BOOL_STATEMENT, INSERT_OR_UPDATE_STR_STATEMENT, INSERT_OR_UPDATE_LONG_STATEMENT, INSERT_OR_UPDATE_DBL_STATEMENT);  
51 - }  
52 -  
53 - @Override  
54 - protected void saveOrUpdateBoolean(TsKvEntity entity, String query) {  
55 - entityManager.createNativeQuery(query)  
56 - .setParameter("entity_type", entity.getEntityType().name())  
57 - .setParameter("entity_id", entity.getEntityId())  
58 - .setParameter("key", entity.getKey())  
59 - .setParameter("ts", entity.getTs())  
60 - .setParameter("bool_v", entity.getBooleanValue())  
61 - .executeUpdate();  
62 - }  
63 -  
64 - @Override  
65 - protected void saveOrUpdateString(TsKvEntity entity, String query) {  
66 - entityManager.createNativeQuery(query)  
67 - .setParameter("entity_type", entity.getEntityType().name())  
68 - .setParameter("entity_id", entity.getEntityId())  
69 - .setParameter("key", entity.getKey())  
70 - .setParameter("ts", entity.getTs())  
71 - .setParameter("str_v", replaceNullChars(entity.getStrValue()))  
72 - .executeUpdate();  
73 - }  
74 -  
75 - @Override  
76 - protected void saveOrUpdateLong(TsKvEntity entity, String query) {  
77 - entityManager.createNativeQuery(query)  
78 - .setParameter("entity_type", entity.getEntityType().name())  
79 - .setParameter("entity_id", entity.getEntityId())  
80 - .setParameter("key", entity.getKey())  
81 - .setParameter("ts", entity.getTs())  
82 - .setParameter("long_v", entity.getLongValue())  
83 - .executeUpdate();  
84 - }  
85 -  
86 - @Override  
87 - protected void saveOrUpdateDouble(TsKvEntity entity, String query) {  
88 - entityManager.createNativeQuery(query)  
89 - .setParameter("entity_type", entity.getEntityType().name())  
90 - .setParameter("entity_id", entity.getEntityId())  
91 - .setParameter("key", entity.getKey())  
92 - .setParameter("ts", entity.getTs())  
93 - .setParameter("dbl_v", entity.getDoubleValue())  
94 - .executeUpdate();  
95 - }  
96 -  
97 - @Override  
98 - public void saveOrUpdate(List<TsKvEntity> entities) {  
99 - jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() {  
100 - @Override  
101 - public void setValues(PreparedStatement ps, int i) throws SQLException {  
102 - TsKvEntity tsKvEntity = entities.get(i);  
103 - ps.setString(1, tsKvEntity.getEntityType().name());  
104 - ps.setString(2, tsKvEntity.getEntityId());  
105 - ps.setString(3, tsKvEntity.getKey());  
106 - ps.setLong(4, tsKvEntity.getTs());  
107 -  
108 - if (tsKvEntity.getBooleanValue() != null) {  
109 - ps.setBoolean(5, tsKvEntity.getBooleanValue());  
110 - ps.setBoolean(9, tsKvEntity.getBooleanValue());  
111 - } else {  
112 - ps.setNull(5, Types.BOOLEAN);  
113 - ps.setNull(9, Types.BOOLEAN);  
114 - }  
115 -  
116 - ps.setString(6, replaceNullChars(tsKvEntity.getStrValue()));  
117 - ps.setString(10, replaceNullChars(tsKvEntity.getStrValue()));  
118 -  
119 -  
120 - if (tsKvEntity.getLongValue() != null) {  
121 - ps.setLong(7, tsKvEntity.getLongValue());  
122 - ps.setLong(11, tsKvEntity.getLongValue());  
123 - } else {  
124 - ps.setNull(7, Types.BIGINT);  
125 - ps.setNull(11, Types.BIGINT);  
126 - }  
127 -  
128 - if (tsKvEntity.getDoubleValue() != null) {  
129 - ps.setDouble(8, tsKvEntity.getDoubleValue());  
130 - ps.setDouble(12, tsKvEntity.getDoubleValue());  
131 - } else {  
132 - ps.setNull(8, Types.DOUBLE);  
133 - ps.setNull(12, Types.DOUBLE);  
134 - }  
135 - }  
136 -  
137 - @Override  
138 - public int getBatchSize() {  
139 - return entities.size();  
140 - }  
141 - });  
142 - }  
143 -}  
@@ -97,7 +97,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -97,7 +97,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
97 @Value("${cassandra.query.set_null_values_enabled}") 97 @Value("${cassandra.query.set_null_values_enabled}")
98 private boolean setNullValuesEnabled; 98 private boolean setNullValuesEnabled;
99 99
100 - private TsPartitionDate tsFormat; 100 + private NoSqlTsPartitionDate tsFormat;
101 101
102 private PreparedStatement partitionInsertStmt; 102 private PreparedStatement partitionInsertStmt;
103 private PreparedStatement partitionInsertTtlStmt; 103 private PreparedStatement partitionInsertTtlStmt;
@@ -120,7 +120,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem @@ -120,7 +120,7 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem
120 super.startExecutor(); 120 super.startExecutor();
121 if (!isInstall()) { 121 if (!isInstall()) {
122 getFetchStmt(Aggregation.NONE, DESC_ORDER); 122 getFetchStmt(Aggregation.NONE, DESC_ORDER);
123 - Optional<TsPartitionDate> partition = TsPartitionDate.parse(partitioning); 123 + Optional<NoSqlTsPartitionDate> partition = NoSqlTsPartitionDate.parse(partitioning);
124 if (partition.isPresent()) { 124 if (partition.isPresent()) {
125 tsFormat = partition.get(); 125 tsFormat = partition.get();
126 } else { 126 } else {
dao/src/main/java/org/thingsboard/server/dao/timeseries/NoSqlTsPartitionDate.java renamed from dao/src/main/java/org/thingsboard/server/dao/timeseries/TsPartitionDate.java
@@ -21,7 +21,7 @@ import java.time.temporal.ChronoUnit; @@ -21,7 +21,7 @@ import java.time.temporal.ChronoUnit;
21 import java.time.temporal.TemporalUnit; 21 import java.time.temporal.TemporalUnit;
22 import java.util.Optional; 22 import java.util.Optional;
23 23
24 -public enum TsPartitionDate { 24 +public enum NoSqlTsPartitionDate {
25 25
26 MINUTES("yyyy-MM-dd-HH-mm", ChronoUnit.MINUTES), HOURS("yyyy-MM-dd-HH", ChronoUnit.HOURS), DAYS("yyyy-MM-dd", ChronoUnit.DAYS), MONTHS("yyyy-MM", ChronoUnit.MONTHS), YEARS("yyyy", ChronoUnit.YEARS),INDEFINITE("",ChronoUnit.FOREVER); 26 MINUTES("yyyy-MM-dd-HH-mm", ChronoUnit.MINUTES), HOURS("yyyy-MM-dd-HH", ChronoUnit.HOURS), DAYS("yyyy-MM-dd", ChronoUnit.DAYS), MONTHS("yyyy-MM", ChronoUnit.MONTHS), YEARS("yyyy", ChronoUnit.YEARS),INDEFINITE("",ChronoUnit.FOREVER);
27 27
@@ -29,7 +29,7 @@ public enum TsPartitionDate { @@ -29,7 +29,7 @@ public enum TsPartitionDate {
29 private final transient TemporalUnit truncateUnit; 29 private final transient TemporalUnit truncateUnit;
30 public final static LocalDateTime EPOCH_START = LocalDateTime.ofEpochSecond(0,0, ZoneOffset.UTC); 30 public final static LocalDateTime EPOCH_START = LocalDateTime.ofEpochSecond(0,0, ZoneOffset.UTC);
31 31
32 - TsPartitionDate(String pattern, TemporalUnit truncateUnit) { 32 + NoSqlTsPartitionDate(String pattern, TemporalUnit truncateUnit) {
33 this.pattern = pattern; 33 this.pattern = pattern;
34 this.truncateUnit = truncateUnit; 34 this.truncateUnit = truncateUnit;
35 } 35 }
@@ -56,10 +56,10 @@ public enum TsPartitionDate { @@ -56,10 +56,10 @@ public enum TsPartitionDate {
56 } 56 }
57 } 57 }
58 58
59 - public static Optional<TsPartitionDate> parse(String name) {  
60 - TsPartitionDate partition = null; 59 + public static Optional<NoSqlTsPartitionDate> parse(String name) {
  60 + NoSqlTsPartitionDate partition = null;
61 if (name != null) { 61 if (name != null) {
62 - for (TsPartitionDate partitionDate : TsPartitionDate.values()) { 62 + for (NoSqlTsPartitionDate partitionDate : NoSqlTsPartitionDate.values()) {
63 if (partitionDate.name().equalsIgnoreCase(name)) { 63 if (partitionDate.name().equalsIgnoreCase(name)) {
64 partition = partitionDate; 64 partition = partitionDate;
65 break; 65 break;
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.timeseries;
  17 +
  18 +import lombok.Data;
  19 +
  20 +import java.text.SimpleDateFormat;
  21 +import java.util.Date;
  22 +
  23 +@Data
  24 +public class PsqlPartition {
  25 +
  26 + private static final String TABLE_REGEX = "ts_kv_";
  27 +
  28 + private long start;
  29 + private long end;
  30 + private String partitionDate;
  31 + private String query;
  32 +
  33 + public PsqlPartition(long start, long end, String pattern) {
  34 + this.start = start;
  35 + this.end = end;
  36 + this.partitionDate = new SimpleDateFormat(pattern).format(new Date(start));
  37 + this.query = createStatement(start, end, partitionDate);
  38 + }
  39 +
  40 + private String createStatement(long start, long end, String partitionDate) {
  41 + return "CREATE TABLE IF NOT EXISTS " + TABLE_REGEX + partitionDate + " PARTITION OF ts_kv(PRIMARY KEY (entity_id, key, ts)) FOR VALUES FROM (" + start + ") TO (" + end + ")";
  42 + }
  43 +}
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.dao.timeseries;
  17 +
  18 +import java.time.LocalDateTime;
  19 +import java.time.ZoneOffset;
  20 +import java.time.temporal.ChronoUnit;
  21 +import java.time.temporal.TemporalUnit;
  22 +import java.util.Optional;
  23 +
  24 +public enum SqlTsPartitionDate {
  25 +
  26 + MINUTES("yyyy_MM_dd_HH_mm", ChronoUnit.MINUTES), HOURS("yyyy_MM_dd_HH", ChronoUnit.HOURS), DAYS("yyyy_MM_dd", ChronoUnit.DAYS), MONTHS("yyyy_MM", ChronoUnit.MONTHS), YEARS("yyyy", ChronoUnit.YEARS), INDEFINITE("indefinite", ChronoUnit.FOREVER);
  27 +
  28 + private final String pattern;
  29 + private final transient TemporalUnit truncateUnit;
  30 + public final static LocalDateTime EPOCH_START = LocalDateTime.ofEpochSecond(0, 0, ZoneOffset.UTC);
  31 +
  32 + SqlTsPartitionDate(String pattern, TemporalUnit truncateUnit) {
  33 + this.pattern = pattern;
  34 + this.truncateUnit = truncateUnit;
  35 + }
  36 +
  37 + public String getPattern() {
  38 + return pattern;
  39 + }
  40 +
  41 + public TemporalUnit getTruncateUnit() {
  42 + return truncateUnit;
  43 + }
  44 +
  45 + public LocalDateTime trancateTo(LocalDateTime time) {
  46 + switch (this) {
  47 + case MINUTES:
  48 + return time.truncatedTo(ChronoUnit.MINUTES);
  49 + case HOURS:
  50 + return time.truncatedTo(ChronoUnit.HOURS);
  51 + case DAYS:
  52 + return time.truncatedTo(ChronoUnit.DAYS);
  53 + case MONTHS:
  54 + return time.truncatedTo(ChronoUnit.DAYS).withDayOfMonth(1);
  55 + case YEARS:
  56 + return time.truncatedTo(ChronoUnit.DAYS).withDayOfYear(1);
  57 + case INDEFINITE:
  58 + return EPOCH_START;
  59 + default:
  60 + throw new RuntimeException("Failed to parse partitioning property!");
  61 + }
  62 + }
  63 +
  64 + public LocalDateTime plusTo(LocalDateTime time) {
  65 + switch (this) {
  66 + case MINUTES:
  67 + return time.plusMinutes(1);
  68 + case HOURS:
  69 + return time.plusHours(1);
  70 + case DAYS:
  71 + return time.plusDays(1);
  72 + case MONTHS:
  73 + return time.plusMonths(1);
  74 + case YEARS:
  75 + return time.plusYears(1);
  76 + default:
  77 + throw new RuntimeException("Failed to parse partitioning property!");
  78 + }
  79 + }
  80 +
  81 + public static Optional<SqlTsPartitionDate> parse(String name) {
  82 + SqlTsPartitionDate partition = null;
  83 + if (name != null) {
  84 + for (SqlTsPartitionDate partitionDate : SqlTsPartitionDate.values()) {
  85 + if (partitionDate.name().equalsIgnoreCase(name)) {
  86 + partition = partitionDate;
  87 + break;
  88 + }
  89 + }
  90 + }
  91 + return Optional.of(partition);
  92 + }
  93 +}
@@ -17,7 +17,25 @@ @@ -17,7 +17,25 @@
17 CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE; 17 CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;
18 18
19 CREATE TABLE IF NOT EXISTS tenant_ts_kv ( 19 CREATE TABLE IF NOT EXISTS tenant_ts_kv (
20 - tenant_id varchar(31) NOT NULL, 20 + tenant_id uuid NOT NULL,
  21 + entity_id uuid NOT NULL,
  22 + key int NOT NULL,
  23 + ts bigint NOT NULL,
  24 + bool_v boolean,
  25 + str_v varchar(10000000),
  26 + long_v bigint,
  27 + dbl_v double precision,
  28 + CONSTRAINT ts_kv_pkey PRIMARY KEY (tenant_id, entity_id, key, ts)
  29 +);
  30 +
  31 +CREATE TABLE IF NOT EXISTS ts_kv_dictionary (
  32 + key varchar(255) NOT NULL,
  33 + key_id serial UNIQUE,
  34 + CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
  35 +);
  36 +
  37 +CREATE TABLE IF NOT EXISTS ts_kv_latest (
  38 + entity_type varchar(255) NOT NULL,
21 entity_id varchar(31) NOT NULL, 39 entity_id varchar(31) NOT NULL,
22 key varchar(255) NOT NULL, 40 key varchar(255) NOT NULL,
23 ts bigint NOT NULL, 41 ts bigint NOT NULL,
@@ -25,7 +43,7 @@ CREATE TABLE IF NOT EXISTS tenant_ts_kv ( @@ -25,7 +43,7 @@ CREATE TABLE IF NOT EXISTS tenant_ts_kv (
25 str_v varchar(10000000), 43 str_v varchar(10000000),
26 long_v bigint, 44 long_v bigint,
27 dbl_v double precision, 45 dbl_v double precision,
28 - CONSTRAINT ts_kv_pkey PRIMARY KEY (tenant_id, entity_id, key, ts) 46 + CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_type, entity_id, key)
29 ); 47 );
30 48
31 SELECT create_hypertable('tenant_ts_kv', 'ts', chunk_time_interval => 86400000, if_not_exists => true); 49 SELECT create_hypertable('tenant_ts_kv', 'ts', chunk_time_interval => 86400000, if_not_exists => true);
dao/src/main/resources/sql/schema-ts-hsql.sql renamed from dao/src/main/resources/sql/schema-ts.sql
  1 +--
  2 +-- Copyright © 2016-2020 The Thingsboard Authors
  3 +--
  4 +-- Licensed under the Apache License, Version 2.0 (the "License");
  5 +-- you may not use this file except in compliance with the License.
  6 +-- You may obtain a copy of the License at
  7 +--
  8 +-- http://www.apache.org/licenses/LICENSE-2.0
  9 +--
  10 +-- Unless required by applicable law or agreed to in writing, software
  11 +-- distributed under the License is distributed on an "AS IS" BASIS,
  12 +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +-- See the License for the specific language governing permissions and
  14 +-- limitations under the License.
  15 +--
  16 +
  17 +CREATE TABLE IF NOT EXISTS ts_kv (
  18 + entity_id uuid NOT NULL,
  19 + key int NOT NULL,
  20 + ts bigint NOT NULL,
  21 + bool_v boolean,
  22 + str_v varchar(10000000),
  23 + long_v bigint,
  24 + dbl_v double precision
  25 +) PARTITION BY RANGE (ts);
  26 +
  27 +CREATE TABLE IF NOT EXISTS ts_kv_dictionary (
  28 + key varchar(255) NOT NULL,
  29 + key_id serial UNIQUE,
  30 + CONSTRAINT ts_key_id_pkey PRIMARY KEY (key)
  31 +);
  32 +
  33 +CREATE TABLE IF NOT EXISTS ts_kv_latest (
  34 + entity_type varchar(255) NOT NULL,
  35 + entity_id varchar(31) NOT NULL,
  36 + key varchar(255) NOT NULL,
  37 + ts bigint NOT NULL,
  38 + bool_v boolean,
  39 + str_v varchar(10000000),
  40 + long_v bigint,
  41 + dbl_v double precision,
  42 + CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_type, entity_id, key)
  43 +);
@@ -30,7 +30,7 @@ import org.springframework.test.context.support.DirtiesContextTestExecutionListe @@ -30,7 +30,7 @@ import org.springframework.test.context.support.DirtiesContextTestExecutionListe
30 * Created by Valerii Sosliuk on 4/22/2017. 30 * Created by Valerii Sosliuk on 4/22/2017.
31 */ 31 */
32 @RunWith(SpringRunner.class) 32 @RunWith(SpringRunner.class)
33 -@ContextConfiguration(classes = {JpaDaoConfig.class, SqlTsDaoConfig.class, JpaDbunitTestConfig.class}) 33 +@ContextConfiguration(classes = {JpaDaoConfig.class, HsqlTsDaoConfig.class, JpaDbunitTestConfig.class})
34 @TestPropertySource("classpath:sql-test.properties") 34 @TestPropertySource("classpath:sql-test.properties")
35 @TestExecutionListeners({ 35 @TestExecutionListeners({
36 DependencyInjectionTestExecutionListener.class, 36 DependencyInjectionTestExecutionListener.class,
@@ -30,9 +30,23 @@ public class JpaDaoTestSuite { @@ -30,9 +30,23 @@ public class JpaDaoTestSuite {
30 30
31 @ClassRule 31 @ClassRule
32 public static CustomSqlUnit sqlUnit = new CustomSqlUnit( 32 public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
33 - Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/system-data.sql"), 33 + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"),
34 "sql/drop-all-tables.sql", 34 "sql/drop-all-tables.sql",
35 "sql-test.properties" 35 "sql-test.properties"
36 ); 36 );
37 37
  38 +// @ClassRule
  39 +// public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
  40 +// Arrays.asList("sql/schema-ts-psql.sql", "sql/schema-entities.sql", "sql/system-data.sql"),
  41 +// "sql/drop-all-tables.sql",
  42 +// "sql-test.properties"
  43 +// );
  44 +
  45 +// @ClassRule
  46 +// public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
  47 +// Arrays.asList("sql/schema-timescale.sql", "sql/schema-timescale-idx.sql", "sql/schema-entities.sql", "sql/system-data.sql"),
  48 +// "sql/timescale/drop-all-tables.sql",
  49 +// "sql-test.properties"
  50 +// );
  51 +
38 } 52 }
@@ -30,9 +30,23 @@ public class SqlDaoServiceTestSuite { @@ -30,9 +30,23 @@ public class SqlDaoServiceTestSuite {
30 30
31 @ClassRule 31 @ClassRule
32 public static CustomSqlUnit sqlUnit = new CustomSqlUnit( 32 public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
33 - Arrays.asList("sql/schema-ts.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"), 33 + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"),
34 "sql/drop-all-tables.sql", 34 "sql/drop-all-tables.sql",
35 "sql-test.properties" 35 "sql-test.properties"
36 ); 36 );
37 37
  38 +// @ClassRule
  39 +// public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
  40 +// Arrays.asList("sql/schema-ts-psql.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"),
  41 +// "sql/drop-all-tables.sql",
  42 +// "sql-test.properties"
  43 +// );
  44 +
  45 +// @ClassRule
  46 +// public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
  47 +// Arrays.asList("sql/schema-timescale.sql", "sql/schema-timescale-idx.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"),
  48 +// "sql/timescale/drop-all-tables.sql",
  49 +// "sql-test.properties"
  50 +// );
  51 +
38 } 52 }
@@ -2,7 +2,8 @@ database.ts.type=sql @@ -2,7 +2,8 @@ database.ts.type=sql
2 database.entities.type=sql 2 database.entities.type=sql
3 3
4 sql.ts_inserts_executor_type=fixed 4 sql.ts_inserts_executor_type=fixed
5 -sql.ts_inserts_fixed_thread_pool_size=10 5 +sql.ts_inserts_fixed_thread_pool_size=200
  6 +sql.ts_key_value_partitioning=MONTHS
6 7
7 spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true 8 spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
8 spring.jpa.show-sql=false 9 spring.jpa.show-sql=false
@@ -13,4 +14,23 @@ spring.datasource.username=sa @@ -13,4 +14,23 @@ spring.datasource.username=sa
13 spring.datasource.password= 14 spring.datasource.password=
14 spring.datasource.url=jdbc:hsqldb:file:/tmp/testDb;sql.enforce_size=false 15 spring.datasource.url=jdbc:hsqldb:file:/tmp/testDb;sql.enforce_size=false
15 spring.datasource.driverClassName=org.hsqldb.jdbc.JDBCDriver 16 spring.datasource.driverClassName=org.hsqldb.jdbc.JDBCDriver
16 -spring.datasource.hikari.maximumPoolSize = 50  
  17 +spring.datasource.hikari.maximumPoolSize = 50
  18 +
  19 +#database.ts.type=timescale
  20 +#database.ts.type=sql
  21 +#database.entities.type=sql
  22 +#
  23 +#sql.ts_inserts_executor_type=fixed
  24 +#sql.ts_inserts_fixed_thread_pool_size=200
  25 +#sql.ts_key_value_partitioning=MONTHS
  26 +#
  27 +#spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
  28 +#spring.jpa.show-sql=false
  29 +#spring.jpa.hibernate.ddl-auto=none
  30 +#spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
  31 +#
  32 +#spring.datasource.username=postgres
  33 +#spring.datasource.password=postgres
  34 +#spring.datasource.url=jdbc:postgresql://localhost:5432/sqltest
  35 +#spring.datasource.driverClassName=org.postgresql.Driver
  36 +#spring.datasource.hikari.maximumPoolSize = 50
@@ -13,6 +13,7 @@ DROP TABLE IF EXISTS relation; @@ -13,6 +13,7 @@ DROP TABLE IF EXISTS relation;
13 DROP TABLE IF EXISTS tb_user; 13 DROP TABLE IF EXISTS tb_user;
14 DROP TABLE IF EXISTS tenant; 14 DROP TABLE IF EXISTS tenant;
15 DROP TABLE IF EXISTS tenant_ts_kv; 15 DROP TABLE IF EXISTS tenant_ts_kv;
  16 +DROP TABLE IF EXISTS ts_kv_latest;
16 DROP TABLE IF EXISTS user_credentials; 17 DROP TABLE IF EXISTS user_credentials;
17 DROP TABLE IF EXISTS widget_type; 18 DROP TABLE IF EXISTS widget_type;
18 DROP TABLE IF EXISTS widgets_bundle; 19 DROP TABLE IF EXISTS widgets_bundle;
@@ -19,7 +19,7 @@ version: '2.2' @@ -19,7 +19,7 @@ version: '2.2'
19 services: 19 services:
20 postgres: 20 postgres:
21 restart: always 21 restart: always
22 - image: "postgres:9.6" 22 + image: "postgres:10"
23 ports: 23 ports:
24 - "5432" 24 - "5432"
25 environment: 25 environment:
@@ -51,7 +51,7 @@ spec: @@ -51,7 +51,7 @@ spec:
51 containers: 51 containers:
52 - name: postgres 52 - name: postgres
53 imagePullPolicy: Always 53 imagePullPolicy: Always
54 - image: postgres:9.6 54 + image: postgres:10
55 ports: 55 ports:
56 - containerPort: 5432 56 - containerPort: 5432
57 name: postgres 57 name: postgres
@@ -20,10 +20,10 @@ firstlaunch=${DATA_FOLDER}/.firstlaunch @@ -20,10 +20,10 @@ firstlaunch=${DATA_FOLDER}/.firstlaunch
20 if [ ! -d ${PGDATA} ]; then 20 if [ ! -d ${PGDATA} ]; then
21 mkdir -p ${PGDATA} 21 mkdir -p ${PGDATA}
22 chown -R postgres:postgres ${PGDATA} 22 chown -R postgres:postgres ${PGDATA}
23 - su postgres -c '/usr/lib/postgresql/9.6/bin/pg_ctl initdb -U postgres' 23 + su postgres -c '/usr/lib/postgresql/10/bin/pg_ctl initdb -U postgres'
24 fi 24 fi
25 25
26 -su postgres -c '/usr/lib/postgresql/9.6/bin/pg_ctl -l /var/log/postgres/postgres.log -w start' 26 +su postgres -c '/usr/lib/postgresql/10/bin/pg_ctl -l /var/log/postgres/postgres.log -w start'
27 27
28 if [ ! -f ${firstlaunch} ]; then 28 if [ ! -f ${firstlaunch} ]; then
29 su postgres -c 'psql -U postgres -d postgres -c "CREATE DATABASE thingsboard"' 29 su postgres -c 'psql -U postgres -d postgres -c "CREATE DATABASE thingsboard"'
@@ -15,4 +15,4 @@ @@ -15,4 +15,4 @@
15 # limitations under the License. 15 # limitations under the License.
16 # 16 #
17 17
18 -su postgres -c '/usr/lib/postgresql/9.6/bin/pg_ctl stop' 18 +su postgres -c '/usr/lib/postgresql/10/bin/pg_ctl stop'