Commit 6af34cf043da68943fd4aa26c608e548c3140776

Authored by ShvaykaD
Committed by Andrew Shvayka
1 parent 083dc38c

refactoring ttl cleanup services

@@ -18,17 +18,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar @@ -18,17 +18,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar
18 LANGUAGE plpgsql AS 18 LANGUAGE plpgsql AS
19 $$ 19 $$
20 DECLARE 20 DECLARE
21 - max_tenant_ttl bigint;  
22 - max_customer_ttl bigint;  
23 - max_ttl bigint;  
24 - date timestamp;  
25 - partition_by_max_ttl_date varchar;  
26 - partition_month varchar;  
27 - partition_day varchar;  
28 - partition_year varchar;  
29 - partition varchar;  
30 - partition_to_delete varchar;  
31 - 21 + max_tenant_ttl bigint;
  22 + max_customer_ttl bigint;
  23 + max_ttl bigint;
  24 + date timestamp;
  25 + partition_by_max_ttl_date varchar;
  26 + partition_by_max_ttl_month varchar;
  27 + partition_by_max_ttl_day varchar;
  28 + partition_by_max_ttl_year varchar;
  29 + partition varchar;
  30 + partition_year integer;
  31 + partition_month integer;
  32 + partition_day integer;
32 33
33 BEGIN 34 BEGIN
34 SELECT max(attribute_kv.long_v) 35 SELECT max(attribute_kv.long_v)
@@ -45,53 +46,138 @@ BEGIN @@ -45,53 +46,138 @@ BEGIN
45 if max_ttl IS NOT NULL AND max_ttl > 0 THEN 46 if max_ttl IS NOT NULL AND max_ttl > 0 THEN
46 date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl); 47 date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl);
47 partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date); 48 partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date);
  49 + RAISE NOTICE 'Date by max ttl: %', date;
48 RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date; 50 RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date;
49 IF partition_by_max_ttl_date IS NOT NULL THEN 51 IF partition_by_max_ttl_date IS NOT NULL THEN
50 CASE 52 CASE
51 WHEN partition_type = 'DAYS' THEN 53 WHEN partition_type = 'DAYS' THEN
52 - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);  
53 - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);  
54 - partition_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); 54 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
  55 + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
  56 + partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5);
55 WHEN partition_type = 'MONTHS' THEN 57 WHEN partition_type = 'MONTHS' THEN
56 - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);  
57 - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); 58 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
  59 + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
58 ELSE 60 ELSE
59 - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); 61 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
60 END CASE; 62 END CASE;
61 - FOR partition IN SELECT tablename  
62 - FROM pg_tables  
63 - WHERE schemaname = 'public'  
64 - AND tablename like 'ts_kv_' || '%'  
65 - AND tablename != 'ts_kv_latest'  
66 - AND tablename != 'ts_kv_dictionary'  
67 - AND tablename != 'ts_kv_indefinite'  
68 - LOOP  
69 - IF partition != partition_by_max_ttl_date THEN  
70 - IF partition_year IS NOT NULL THEN  
71 - IF SPLIT_PART(partition, '_', 3)::integer < partition_year::integer THEN  
72 - partition_to_delete := partition;  
73 - ELSE  
74 - IF partition_month IS NOT NULL THEN  
75 - IF SPLIT_PART(partition, '_', 4)::integer < partition_month::integer THEN  
76 - partition_to_delete := partition; 63 + IF partition_by_max_ttl_year IS NULL THEN
  64 + RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!';
  65 + ELSE
  66 + IF partition_type = 'YEARS' THEN
  67 + FOR partition IN SELECT tablename
  68 + FROM pg_tables
  69 + WHERE schemaname = 'public'
  70 + AND tablename like 'ts_kv_' || '%'
  71 + AND tablename != 'ts_kv_latest'
  72 + AND tablename != 'ts_kv_dictionary'
  73 + AND tablename != 'ts_kv_indefinite'
  74 + AND tablename != partition_by_max_ttl_date
  75 + LOOP
  76 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  77 + IF partition_year < partition_by_max_ttl_year::integer THEN
  78 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  79 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  80 + deleted := deleted + 1;
  81 + END IF;
  82 + END LOOP;
  83 + ELSE
  84 + IF partition_type = 'MONTHS' THEN
  85 + IF partition_by_max_ttl_month IS NULL THEN
  86 + RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!';
  87 + ELSE
  88 + FOR partition IN SELECT tablename
  89 + FROM pg_tables
  90 + WHERE schemaname = 'public'
  91 + AND tablename like 'ts_kv_' || '%'
  92 + AND tablename != 'ts_kv_latest'
  93 + AND tablename != 'ts_kv_dictionary'
  94 + AND tablename != 'ts_kv_indefinite'
  95 + AND tablename != partition_by_max_ttl_date
  96 + LOOP
  97 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  98 + IF partition_year > partition_by_max_ttl_year::integer THEN
  99 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  100 + CONTINUE;
77 ELSE 101 ELSE
78 - IF partition_day IS NOT NULL THEN  
79 - IF SPLIT_PART(partition, '_', 5)::integer < partition_day::integer THEN  
80 - partition_to_delete := partition; 102 + IF partition_year < partition_by_max_ttl_year::integer THEN
  103 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  104 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  105 + deleted := deleted + 1;
  106 + ELSE
  107 + partition_month := SPLIT_PART(partition, '_', 4)::integer;
  108 + IF partition_year = partition_by_max_ttl_year::integer THEN
  109 + IF partition_month >= partition_by_max_ttl_month::integer THEN
  110 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  111 + CONTINUE;
  112 + ELSE
  113 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  114 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  115 + deleted := deleted + 1;
  116 + END IF;
81 END IF; 117 END IF;
82 END IF; 118 END IF;
83 END IF; 119 END IF;
  120 + END LOOP;
  121 + END IF;
  122 + ELSE
  123 + IF partition_type = 'DAYS' THEN
  124 + IF partition_by_max_ttl_month IS NULL THEN
  125 + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!';
  126 + ELSE
  127 + IF partition_by_max_ttl_day IS NULL THEN
  128 + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!';
  129 + ELSE
  130 + FOR partition IN SELECT tablename
  131 + FROM pg_tables
  132 + WHERE schemaname = 'public'
  133 + AND tablename like 'ts_kv_' || '%'
  134 + AND tablename != 'ts_kv_latest'
  135 + AND tablename != 'ts_kv_dictionary'
  136 + AND tablename != 'ts_kv_indefinite'
  137 + AND tablename != partition_by_max_ttl_date
  138 + LOOP
  139 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  140 + IF partition_year > partition_by_max_ttl_year::integer THEN
  141 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  142 + CONTINUE;
  143 + ELSE
  144 + IF partition_year < partition_by_max_ttl_year::integer THEN
  145 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  146 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  147 + deleted := deleted + 1;
  148 + ELSE
  149 + partition_month := SPLIT_PART(partition, '_', 4)::integer;
  150 + IF partition_month > partition_by_max_ttl_month::integer THEN
  151 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  152 + CONTINUE;
  153 + ELSE
  154 + IF partition_month < partition_by_max_ttl_month::integer THEN
  155 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  156 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  157 + deleted := deleted + 1;
  158 + ELSE
  159 + partition_day := SPLIT_PART(partition, '_', 5)::integer;
  160 + IF partition_day >= partition_by_max_ttl_day::integer THEN
  161 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  162 + CONTINUE;
  163 + ELSE
  164 + IF partition_day < partition_by_max_ttl_day::integer THEN
  165 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  166 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  167 + deleted := deleted + 1;
  168 + END IF;
  169 + END IF;
  170 + END IF;
  171 + END IF;
  172 + END IF;
  173 + END IF;
  174 + END LOOP;
84 END IF; 175 END IF;
85 END IF; 176 END IF;
86 END IF; 177 END IF;
87 - IF partition_to_delete IS NOT NULL THEN  
88 - RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete;  
89 - EXECUTE format('DROP TABLE IF EXISTS %I', partition_to_delete);  
90 - partition_to_delete := NULL;  
91 - deleted := deleted + 1;  
92 - END IF;  
93 END IF; 178 END IF;
94 - END LOOP; 179 + END IF;
  180 + END IF;
95 END IF; 181 END IF;
96 END IF; 182 END IF;
97 END 183 END
@@ -107,8 +193,6 @@ BEGIN @@ -107,8 +193,6 @@ BEGIN
107 partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM'); 193 partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM');
108 WHEN partition_type = 'YEARS' THEN 194 WHEN partition_type = 'YEARS' THEN
109 partition := 'ts_kv_' || to_char(date, 'yyyy'); 195 partition := 'ts_kv_' || to_char(date, 'yyyy');
110 - WHEN partition_type = 'INDEFINITE' THEN  
111 - partition := NULL;  
112 ELSE 196 ELSE
113 partition := NULL; 197 partition := NULL;
114 END CASE; 198 END CASE;
@@ -45,9 +45,7 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService { @@ -45,9 +45,7 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService {
45 @Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}") 45 @Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}")
46 public void cleanUp() { 46 public void cleanUp() {
47 if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) { 47 if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) {
48 - log.info("Going to cleanup old edge events using ttl: {}s", ttl);  
49 - long totalEdgeEventsRemoved = edgeService.cleanupEvents(ttl);  
50 - log.info("Total edge events removed by TTL: [{}]", totalEdgeEventsRemoved); 48 + edgeService.cleanupEvents(ttl);
51 } 49 }
52 } 50 }
53 51
@@ -48,9 +48,7 @@ public class EventsCleanUpService extends AbstractCleanUpService { @@ -48,9 +48,7 @@ public class EventsCleanUpService extends AbstractCleanUpService {
48 @Scheduled(initialDelayString = "${sql.ttl.events.execution_interval_ms}", fixedDelayString = "${sql.ttl.events.execution_interval_ms}") 48 @Scheduled(initialDelayString = "${sql.ttl.events.execution_interval_ms}", fixedDelayString = "${sql.ttl.events.execution_interval_ms}")
49 public void cleanUp() { 49 public void cleanUp() {
50 if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) { 50 if (ttlTaskExecutionEnabled && isSystemTenantPartitionMine()) {
51 - log.info("Going to cleanup old events using debug events ttl: {}s and other events ttl: {}s", debugTtl, ttl);  
52 - long totalEventsRemoved = eventService.cleanupEvents(ttl, debugTtl);  
53 - log.info("Total events removed by TTL: [{}]", totalEventsRemoved); 51 + eventService.cleanupEvents(ttl, debugTtl);
54 } 52 }
55 } 53 }
56 54
@@ -18,11 +18,15 @@ package org.thingsboard.server.service.ttl; @@ -18,11 +18,15 @@ package org.thingsboard.server.service.ttl;
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.beans.factory.annotation.Value; 19 import org.springframework.beans.factory.annotation.Value;
20 import org.springframework.scheduling.annotation.Scheduled; 20 import org.springframework.scheduling.annotation.Scheduled;
  21 +import org.springframework.stereotype.Service;
21 import org.thingsboard.server.dao.timeseries.TimeseriesService; 22 import org.thingsboard.server.dao.timeseries.TimeseriesService;
22 import org.thingsboard.server.queue.discovery.PartitionService; 23 import org.thingsboard.server.queue.discovery.PartitionService;
  24 +import org.thingsboard.server.queue.util.TbCoreComponent;
23 import org.thingsboard.server.service.ttl.AbstractCleanUpService; 25 import org.thingsboard.server.service.ttl.AbstractCleanUpService;
24 26
  27 +@TbCoreComponent
25 @Slf4j 28 @Slf4j
  29 +@Service
26 public class TimeseriesCleanUpService extends AbstractCleanUpService { 30 public class TimeseriesCleanUpService extends AbstractCleanUpService {
27 31
28 @Value("${sql.ttl.ts.ts_key_value_ttl}") 32 @Value("${sql.ttl.ts.ts_key_value_ttl}")
@@ -94,5 +94,5 @@ public interface EdgeService { @@ -94,5 +94,5 @@ public interface EdgeService {
94 94
95 String findMissingToRelatedRuleChains(TenantId tenantId, EdgeId edgeId); 95 String findMissingToRelatedRuleChains(TenantId tenantId, EdgeId edgeId);
96 96
97 - long cleanupEvents(long ttl); 97 + void cleanupEvents(long ttl);
98 } 98 }
@@ -46,6 +46,6 @@ public interface EventService { @@ -46,6 +46,6 @@ public interface EventService {
46 46
47 void removeEvents(TenantId tenantId, EntityId entityId); 47 void removeEvents(TenantId tenantId, EntityId entityId);
48 48
49 - long cleanupEvents(long ttl, long debugTtl); 49 + void cleanupEvents(long ttl, long debugTtl);
50 50
51 } 51 }
@@ -180,7 +180,6 @@ public interface EdgeDao extends Dao<Edge> { @@ -180,7 +180,6 @@ public interface EdgeDao extends Dao<Edge> {
180 /** 180 /**
181 * Executes stored procedure to cleanup old edge events. 181 * Executes stored procedure to cleanup old edge events.
182 * @param ttl the ttl for edge events in seconds 182 * @param ttl the ttl for edge events in seconds
183 - * @return the number of deleted edge events  
184 */ 183 */
185 - long cleanupEvents(long ttl); 184 + void cleanupEvents(long ttl);
186 } 185 }
@@ -628,8 +628,8 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic @@ -628,8 +628,8 @@ public class EdgeServiceImpl extends AbstractEntityService implements EdgeServic
628 } 628 }
629 629
630 @Override 630 @Override
631 - public long cleanupEvents(long ttl) {  
632 - return edgeDao.cleanupEvents(ttl); 631 + public void cleanupEvents(long ttl) {
  632 + edgeDao.cleanupEvents(ttl);
633 } 633 }
634 634
635 private List<RuleChain> findEdgeRuleChains(TenantId tenantId, EdgeId edgeId) { 635 private List<RuleChain> findEdgeRuleChains(TenantId tenantId, EdgeId edgeId) {
@@ -132,8 +132,8 @@ public class BaseEventService implements EventService { @@ -132,8 +132,8 @@ public class BaseEventService implements EventService {
132 } 132 }
133 133
134 @Override 134 @Override
135 - public long cleanupEvents(long ttl, long debugTtl) {  
136 - return eventDao.cleanupEvents(ttl, debugTtl); 135 + public void cleanupEvents(long ttl, long debugTtl) {
  136 + eventDao.cleanupEvents(ttl, debugTtl);
137 } 137 }
138 138
139 private DataValidator<Event> eventValidator = 139 private DataValidator<Event> eventValidator =
@@ -106,7 +106,6 @@ public interface EventDao extends Dao<Event> { @@ -106,7 +106,6 @@ public interface EventDao extends Dao<Event> {
106 * Executes stored procedure to cleanup old events. Uses separate ttl for debug and other events. 106 * Executes stored procedure to cleanup old events. Uses separate ttl for debug and other events.
107 * @param otherEventsTtl the ttl for events in seconds 107 * @param otherEventsTtl the ttl for events in seconds
108 * @param debugEventsTtl the ttl for debug events in seconds 108 * @param debugEventsTtl the ttl for debug events in seconds
109 - * @return the number of deleted events  
110 */ 109 */
111 - long cleanupEvents(long otherEventsTtl, long debugEventsTtl); 110 + void cleanupEvents(long otherEventsTtl, long debugEventsTtl);
112 } 111 }
@@ -40,9 +40,9 @@ import org.thingsboard.server.dao.model.sql.EdgeInfoEntity; @@ -40,9 +40,9 @@ import org.thingsboard.server.dao.model.sql.EdgeInfoEntity;
40 import org.thingsboard.server.dao.relation.RelationDao; 40 import org.thingsboard.server.dao.relation.RelationDao;
41 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; 41 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
42 42
43 -import java.sql.CallableStatement; 43 +import java.sql.PreparedStatement;
  44 +import java.sql.ResultSet;
44 import java.sql.SQLException; 45 import java.sql.SQLException;
45 -import java.sql.Types;  
46 import java.util.ArrayList; 46 import java.util.ArrayList;
47 import java.util.Collections; 47 import java.util.Collections;
48 import java.util.List; 48 import java.util.List;
@@ -198,17 +198,19 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao<EdgeEntity, Edge> imple @@ -198,17 +198,19 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao<EdgeEntity, Edge> imple
198 } 198 }
199 199
200 @Override 200 @Override
201 - public long cleanupEvents(long ttl) { 201 + public void cleanupEvents(long ttl) {
202 try { 202 try {
203 - CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_edge_events_by_ttl(?,?,?)}"); 203 + log.info("Going to cleanup old edge events using ttl: {}s", ttl);
  204 + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_edge_events_by_ttl(?,?)");
204 stmt.setLong(1, ttl); 205 stmt.setLong(1, ttl);
205 - stmt.registerOutParameter(3, Types.BIGINT);  
206 - stmt.executeUpdate(); 206 + stmt.setLong(2, 0);
  207 + stmt.execute();
207 printWarnings(stmt); 208 printWarnings(stmt);
208 - return stmt.getLong(3); 209 + ResultSet resultSet = stmt.getResultSet();
  210 + resultSet.next();
  211 + log.info("Total edge events removed by TTL: [{}]", resultSet.getLong(1));
209 } catch (SQLException e) { 212 } catch (SQLException e) {
210 - log.error("SQLException occurred during TTL task execution ", e);  
211 - return 0; 213 + log.error("SQLException occurred during edge events TTL task execution ", e);
212 } 214 }
213 } 215 }
214 216
@@ -39,12 +39,9 @@ import org.thingsboard.server.dao.event.EventDao; @@ -39,12 +39,9 @@ import org.thingsboard.server.dao.event.EventDao;
39 import org.thingsboard.server.dao.model.sql.EventEntity; 39 import org.thingsboard.server.dao.model.sql.EventEntity;
40 import org.thingsboard.server.dao.sql.JpaAbstractDao; 40 import org.thingsboard.server.dao.sql.JpaAbstractDao;
41 41
42 -import javax.sql.DataSource;  
43 -import java.sql.CallableStatement; 42 +import java.sql.PreparedStatement;
  43 +import java.sql.ResultSet;
44 import java.sql.SQLException; 44 import java.sql.SQLException;
45 -import java.sql.SQLWarning;  
46 -import java.sql.Statement;  
47 -import java.sql.Types;  
48 import java.util.List; 45 import java.util.List;
49 import java.util.Objects; 46 import java.util.Objects;
50 import java.util.Optional; 47 import java.util.Optional;
@@ -262,18 +259,20 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen @@ -262,18 +259,20 @@ public class JpaBaseEventDao extends JpaAbstractDao<EventEntity, Event> implemen
262 } 259 }
263 260
264 @Override 261 @Override
265 - public long cleanupEvents(long otherEventsTtl, long debugEventsTtl) { 262 + public void cleanupEvents(long otherEventsTtl, long debugEventsTtl) {
266 try { 263 try {
267 - CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_events_by_ttl(?,?,?)}"); 264 + log.info("Going to cleanup old events using debug events ttl: {}s and other events ttl: {}s", debugEventsTtl, otherEventsTtl);
  265 + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_events_by_ttl(?,?,?)");
268 stmt.setLong(1, otherEventsTtl); 266 stmt.setLong(1, otherEventsTtl);
269 stmt.setLong(2, debugEventsTtl); 267 stmt.setLong(2, debugEventsTtl);
270 - stmt.registerOutParameter(3, Types.BIGINT);  
271 - stmt.executeUpdate(); 268 + stmt.setLong(3, 0);
  269 + stmt.execute();
272 printWarnings(stmt); 270 printWarnings(stmt);
273 - return stmt.getLong(3); 271 + ResultSet resultSet = stmt.getResultSet();
  272 + resultSet.next();
  273 + log.info("Total events removed by TTL: [{}]", resultSet.getLong(1));
274 } catch (SQLException e) { 274 } catch (SQLException e) {
275 - log.error("SQLException occurred during TTL task execution ", e);  
276 - return 0; 275 + log.error("SQLException occurred during events TTL task execution ", e);
277 } 276 }
278 } 277 }
279 278
@@ -29,9 +29,9 @@ import org.thingsboard.server.dao.model.ModelConstants; @@ -29,9 +29,9 @@ import org.thingsboard.server.dao.model.ModelConstants;
29 import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent; 29 import org.thingsboard.server.dao.sql.ScheduledLogExecutorComponent;
30 30
31 import javax.annotation.Nullable; 31 import javax.annotation.Nullable;
32 -import java.sql.CallableStatement; 32 +import java.sql.PreparedStatement;
  33 +import java.sql.ResultSet;
33 import java.sql.SQLException; 34 import java.sql.SQLException;
34 -import java.sql.Types;  
35 import java.util.List; 35 import java.util.List;
36 import java.util.Objects; 36 import java.util.Objects;
37 import java.util.concurrent.TimeUnit; 37 import java.util.concurrent.TimeUnit;
@@ -69,15 +69,17 @@ public abstract class AbstractSqlTimeseriesDao extends BaseAbstractSqlTimeseries @@ -69,15 +69,17 @@ public abstract class AbstractSqlTimeseriesDao extends BaseAbstractSqlTimeseries
69 public void cleanup(long systemTtl) { 69 public void cleanup(long systemTtl) {
70 try { 70 try {
71 log.info("Going to cleanup old timeseries data using ttl: {}s", systemTtl); 71 log.info("Going to cleanup old timeseries data using ttl: {}s", systemTtl);
72 - CallableStatement stmt = dataSource.getConnection().prepareCall("{call cleanup_timeseries_by_ttl(?,?,?)}"); 72 + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call cleanup_timeseries_by_ttl(?,?,?)");
73 stmt.setObject(1, ModelConstants.NULL_UUID); 73 stmt.setObject(1, ModelConstants.NULL_UUID);
74 stmt.setLong(2, systemTtl); 74 stmt.setLong(2, systemTtl);
75 - stmt.registerOutParameter(3, Types.BIGINT);  
76 - stmt.executeUpdate(); 75 + stmt.setLong(3, 0);
  76 + stmt.execute();
77 printWarnings(stmt); 77 printWarnings(stmt);
78 - log.info("Total telemetry removed stats by TTL for entities: [{}]", stmt.getLong(3)); 78 + ResultSet resultSet = stmt.getResultSet();
  79 + resultSet.next();
  80 + log.info("Total telemetry removed stats by TTL for entities: [{}]", resultSet.getLong(1));
79 } catch (SQLException e) { 81 } catch (SQLException e) {
80 - log.error("SQLException occurred during TTL task execution ", e); 82 + log.error("SQLException occurred during timeseries TTL task execution ", e);
81 } 83 }
82 } 84 }
83 85
@@ -27,7 +27,6 @@ import org.springframework.stereotype.Component; @@ -27,7 +27,6 @@ import org.springframework.stereotype.Component;
27 import org.thingsboard.server.common.data.id.EntityId; 27 import org.thingsboard.server.common.data.id.EntityId;
28 import org.thingsboard.server.common.data.id.TenantId; 28 import org.thingsboard.server.common.data.id.TenantId;
29 import org.thingsboard.server.common.data.kv.TsKvEntry; 29 import org.thingsboard.server.common.data.kv.TsKvEntry;
30 -import org.thingsboard.server.dao.model.ModelConstants;  
31 import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity; 30 import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity;
32 import org.thingsboard.server.dao.sqlts.AbstractChunkedAggregationTimeseriesDao; 31 import org.thingsboard.server.dao.sqlts.AbstractChunkedAggregationTimeseriesDao;
33 import org.thingsboard.server.dao.sqlts.insert.psql.PsqlPartitioningRepository; 32 import org.thingsboard.server.dao.sqlts.insert.psql.PsqlPartitioningRepository;
@@ -36,9 +35,9 @@ import org.thingsboard.server.dao.timeseries.SqlTsPartitionDate; @@ -36,9 +35,9 @@ import org.thingsboard.server.dao.timeseries.SqlTsPartitionDate;
36 import org.thingsboard.server.dao.util.PsqlDao; 35 import org.thingsboard.server.dao.util.PsqlDao;
37 import org.thingsboard.server.dao.util.SqlTsDao; 36 import org.thingsboard.server.dao.util.SqlTsDao;
38 37
39 -import java.sql.CallableStatement; 38 +import java.sql.PreparedStatement;
  39 +import java.sql.ResultSet;
40 import java.sql.SQLException; 40 import java.sql.SQLException;
41 -import java.sql.Types;  
42 import java.time.Instant; 41 import java.time.Instant;
43 import java.time.LocalDateTime; 42 import java.time.LocalDateTime;
44 import java.time.ZoneOffset; 43 import java.time.ZoneOffset;
@@ -107,13 +106,15 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa @@ -107,13 +106,15 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa
107 private void cleanupPartitions(long systemTtl) { 106 private void cleanupPartitions(long systemTtl) {
108 try { 107 try {
109 log.info("Going to cleanup old timeseries data partitions using partition type: {} and ttl: {}s", partitioning, systemTtl); 108 log.info("Going to cleanup old timeseries data partitions using partition type: {} and ttl: {}s", partitioning, systemTtl);
110 - CallableStatement stmt = dataSource.getConnection().prepareCall("{call drop_partitions_by_max_ttl(?,?,?)}");  
111 - stmt.setObject(1, partitioning); 109 + PreparedStatement stmt = dataSource.getConnection().prepareStatement("call drop_partitions_by_max_ttl(?,?,?)");
  110 + stmt.setString(1, partitioning);
112 stmt.setLong(2, systemTtl); 111 stmt.setLong(2, systemTtl);
113 - stmt.registerOutParameter(3, Types.BIGINT);  
114 - stmt.executeUpdate(); 112 + stmt.setLong(3, 0);
  113 + stmt.execute();
115 printWarnings(stmt); 114 printWarnings(stmt);
116 - log.info("Total partitions removed by TTL: [{}]", stmt.getLong(3)); 115 + ResultSet resultSet = stmt.getResultSet();
  116 + resultSet.next();
  117 + log.info("Total partitions removed by TTL: [{}]", resultSet.getLong(1));
117 } catch (SQLException e) { 118 } catch (SQLException e) {
118 log.error("SQLException occurred during TTL task execution ", e); 119 log.error("SQLException occurred during TTL task execution ", e);
119 } 120 }
@@ -38,17 +38,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar @@ -38,17 +38,18 @@ CREATE OR REPLACE PROCEDURE drop_partitions_by_max_ttl(IN partition_type varchar
38 LANGUAGE plpgsql AS 38 LANGUAGE plpgsql AS
39 $$ 39 $$
40 DECLARE 40 DECLARE
41 - max_tenant_ttl bigint;  
42 - max_customer_ttl bigint;  
43 - max_ttl bigint;  
44 - date timestamp;  
45 - partition_by_max_ttl_date varchar;  
46 - partition_month varchar;  
47 - partition_day varchar;  
48 - partition_year varchar;  
49 - partition varchar;  
50 - partition_to_delete varchar;  
51 - 41 + max_tenant_ttl bigint;
  42 + max_customer_ttl bigint;
  43 + max_ttl bigint;
  44 + date timestamp;
  45 + partition_by_max_ttl_date varchar;
  46 + partition_by_max_ttl_month varchar;
  47 + partition_by_max_ttl_day varchar;
  48 + partition_by_max_ttl_year varchar;
  49 + partition varchar;
  50 + partition_year integer;
  51 + partition_month integer;
  52 + partition_day integer;
52 53
53 BEGIN 54 BEGIN
54 SELECT max(attribute_kv.long_v) 55 SELECT max(attribute_kv.long_v)
@@ -65,53 +66,138 @@ BEGIN @@ -65,53 +66,138 @@ BEGIN
65 if max_ttl IS NOT NULL AND max_ttl > 0 THEN 66 if max_ttl IS NOT NULL AND max_ttl > 0 THEN
66 date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl); 67 date := to_timestamp(EXTRACT(EPOCH FROM current_timestamp) - max_ttl);
67 partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date); 68 partition_by_max_ttl_date := get_partition_by_max_ttl_date(partition_type, date);
  69 + RAISE NOTICE 'Date by max ttl: %', date;
68 RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date; 70 RAISE NOTICE 'Partition by max ttl: %', partition_by_max_ttl_date;
69 IF partition_by_max_ttl_date IS NOT NULL THEN 71 IF partition_by_max_ttl_date IS NOT NULL THEN
70 CASE 72 CASE
71 WHEN partition_type = 'DAYS' THEN 73 WHEN partition_type = 'DAYS' THEN
72 - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);  
73 - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);  
74 - partition_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5); 74 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
  75 + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
  76 + partition_by_max_ttl_day := SPLIT_PART(partition_by_max_ttl_date, '_', 5);
75 WHEN partition_type = 'MONTHS' THEN 77 WHEN partition_type = 'MONTHS' THEN
76 - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);  
77 - partition_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4); 78 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
  79 + partition_by_max_ttl_month := SPLIT_PART(partition_by_max_ttl_date, '_', 4);
78 ELSE 80 ELSE
79 - partition_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3); 81 + partition_by_max_ttl_year := SPLIT_PART(partition_by_max_ttl_date, '_', 3);
80 END CASE; 82 END CASE;
81 - FOR partition IN SELECT tablename  
82 - FROM pg_tables  
83 - WHERE schemaname = 'public'  
84 - AND tablename like 'ts_kv_' || '%'  
85 - AND tablename != 'ts_kv_latest'  
86 - AND tablename != 'ts_kv_dictionary'  
87 - AND tablename != 'ts_kv_indefinite'  
88 - LOOP  
89 - IF partition != partition_by_max_ttl_date THEN  
90 - IF partition_year IS NOT NULL THEN  
91 - IF SPLIT_PART(partition, '_', 3)::integer < partition_year::integer THEN  
92 - partition_to_delete := partition;  
93 - ELSE  
94 - IF partition_month IS NOT NULL THEN  
95 - IF SPLIT_PART(partition, '_', 4)::integer < partition_month::integer THEN  
96 - partition_to_delete := partition; 83 + IF partition_by_max_ttl_year IS NULL THEN
  84 + RAISE NOTICE 'Failed to remove partitions by max ttl date due to partition_by_max_ttl_year is null!';
  85 + ELSE
  86 + IF partition_type = 'YEARS' THEN
  87 + FOR partition IN SELECT tablename
  88 + FROM pg_tables
  89 + WHERE schemaname = 'public'
  90 + AND tablename like 'ts_kv_' || '%'
  91 + AND tablename != 'ts_kv_latest'
  92 + AND tablename != 'ts_kv_dictionary'
  93 + AND tablename != 'ts_kv_indefinite'
  94 + AND tablename != partition_by_max_ttl_date
  95 + LOOP
  96 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  97 + IF partition_year < partition_by_max_ttl_year::integer THEN
  98 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  99 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  100 + deleted := deleted + 1;
  101 + END IF;
  102 + END LOOP;
  103 + ELSE
  104 + IF partition_type = 'MONTHS' THEN
  105 + IF partition_by_max_ttl_month IS NULL THEN
  106 + RAISE NOTICE 'Failed to remove months partitions by max ttl date due to partition_by_max_ttl_month is null!';
  107 + ELSE
  108 + FOR partition IN SELECT tablename
  109 + FROM pg_tables
  110 + WHERE schemaname = 'public'
  111 + AND tablename like 'ts_kv_' || '%'
  112 + AND tablename != 'ts_kv_latest'
  113 + AND tablename != 'ts_kv_dictionary'
  114 + AND tablename != 'ts_kv_indefinite'
  115 + AND tablename != partition_by_max_ttl_date
  116 + LOOP
  117 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  118 + IF partition_year > partition_by_max_ttl_year::integer THEN
  119 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  120 + CONTINUE;
97 ELSE 121 ELSE
98 - IF partition_day IS NOT NULL THEN  
99 - IF SPLIT_PART(partition, '_', 5)::integer < partition_day::integer THEN  
100 - partition_to_delete := partition; 122 + IF partition_year < partition_by_max_ttl_year::integer THEN
  123 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  124 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  125 + deleted := deleted + 1;
  126 + ELSE
  127 + partition_month := SPLIT_PART(partition, '_', 4)::integer;
  128 + IF partition_year = partition_by_max_ttl_year::integer THEN
  129 + IF partition_month >= partition_by_max_ttl_month::integer THEN
  130 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  131 + CONTINUE;
  132 + ELSE
  133 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  134 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  135 + deleted := deleted + 1;
  136 + END IF;
101 END IF; 137 END IF;
102 END IF; 138 END IF;
103 END IF; 139 END IF;
  140 + END LOOP;
  141 + END IF;
  142 + ELSE
  143 + IF partition_type = 'DAYS' THEN
  144 + IF partition_by_max_ttl_month IS NULL THEN
  145 + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_month is null!';
  146 + ELSE
  147 + IF partition_by_max_ttl_day IS NULL THEN
  148 + RAISE NOTICE 'Failed to remove days partitions by max ttl date due to partition_by_max_ttl_day is null!';
  149 + ELSE
  150 + FOR partition IN SELECT tablename
  151 + FROM pg_tables
  152 + WHERE schemaname = 'public'
  153 + AND tablename like 'ts_kv_' || '%'
  154 + AND tablename != 'ts_kv_latest'
  155 + AND tablename != 'ts_kv_dictionary'
  156 + AND tablename != 'ts_kv_indefinite'
  157 + AND tablename != partition_by_max_ttl_date
  158 + LOOP
  159 + partition_year := SPLIT_PART(partition, '_', 3)::integer;
  160 + IF partition_year > partition_by_max_ttl_year::integer THEN
  161 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  162 + CONTINUE;
  163 + ELSE
  164 + IF partition_year < partition_by_max_ttl_year::integer THEN
  165 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  166 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  167 + deleted := deleted + 1;
  168 + ELSE
  169 + partition_month := SPLIT_PART(partition, '_', 4)::integer;
  170 + IF partition_month > partition_by_max_ttl_month::integer THEN
  171 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  172 + CONTINUE;
  173 + ELSE
  174 + IF partition_month < partition_by_max_ttl_month::integer THEN
  175 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  176 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  177 + deleted := deleted + 1;
  178 + ELSE
  179 + partition_day := SPLIT_PART(partition, '_', 5)::integer;
  180 + IF partition_day >= partition_by_max_ttl_day::integer THEN
  181 + RAISE NOTICE 'Skip iteration! Partition: % is valid!', partition;
  182 + CONTINUE;
  183 + ELSE
  184 + IF partition_day < partition_by_max_ttl_day::integer THEN
  185 + RAISE NOTICE 'Partition to delete by max ttl: %', partition;
  186 + EXECUTE format('DROP TABLE IF EXISTS %I', partition);
  187 + deleted := deleted + 1;
  188 + END IF;
  189 + END IF;
  190 + END IF;
  191 + END IF;
  192 + END IF;
  193 + END IF;
  194 + END LOOP;
104 END IF; 195 END IF;
105 END IF; 196 END IF;
106 END IF; 197 END IF;
107 - IF partition_to_delete IS NOT NULL THEN  
108 - RAISE NOTICE 'Partition to delete by max ttl: %', partition_to_delete;  
109 - EXECUTE format('DROP TABLE IF EXISTS %I', partition_to_delete);  
110 - partition_to_delete := NULL;  
111 - deleted := deleted + 1;  
112 - END IF;  
113 END IF; 198 END IF;
114 - END LOOP; 199 + END IF;
  200 + END IF;
115 END IF; 201 END IF;
116 END IF; 202 END IF;
117 END 203 END
@@ -127,8 +213,6 @@ BEGIN @@ -127,8 +213,6 @@ BEGIN
127 partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM'); 213 partition := 'ts_kv_' || to_char(date, 'yyyy') || '_' || to_char(date, 'MM');
128 WHEN partition_type = 'YEARS' THEN 214 WHEN partition_type = 'YEARS' THEN
129 partition := 'ts_kv_' || to_char(date, 'yyyy'); 215 partition := 'ts_kv_' || to_char(date, 'yyyy');
130 - WHEN partition_type = 'INDEFINITE' THEN  
131 - partition := NULL;  
132 ELSE 216 ELSE
133 partition := NULL; 217 partition := NULL;
134 END CASE; 218 END CASE;