Commit 7f1f29877449ecedfd448c3d0f628acc1c39a8a9

Authored by Sergey Matvienko
Committed by Andrew Shvayka
1 parent a852e11a

cassandra buffered rate executor: separate beans for read and write operations

... ... @@ -59,7 +59,8 @@ import org.thingsboard.server.dao.edge.EdgeEventService;
59 59 import org.thingsboard.server.dao.edge.EdgeService;
60 60 import org.thingsboard.server.dao.entityview.EntityViewService;
61 61 import org.thingsboard.server.dao.event.EventService;
62   -import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor;
  62 +import org.thingsboard.server.dao.nosql.CassandraBufferedRateReadExecutor;
  63 +import org.thingsboard.server.dao.nosql.CassandraBufferedRateWriteExecutor;
63 64 import org.thingsboard.server.dao.ota.OtaPackageService;
64 65 import org.thingsboard.server.dao.relation.RelationService;
65 66 import org.thingsboard.server.dao.resource.ResourceService;
... ... @@ -85,7 +86,6 @@ import org.thingsboard.server.cluster.TbClusterService;
85 86 import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService;
86 87 import org.thingsboard.server.service.rpc.TbRpcService;
87 88 import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService;
88   -import org.thingsboard.server.service.script.JsExecutorService;
89 89 import org.thingsboard.server.service.script.JsInvokeService;
90 90 import org.thingsboard.server.service.session.DeviceSessionCacheService;
91 91 import org.thingsboard.server.service.sms.SmsExecutorService;
... ... @@ -421,7 +421,11 @@ public class ActorSystemContext {
421 421
422 422 @Autowired(required = false)
423 423 @Getter
424   - private CassandraBufferedRateExecutor cassandraBufferedRateExecutor;
  424 + private CassandraBufferedRateReadExecutor cassandraBufferedRateReadExecutor;
  425 +
  426 + @Autowired(required = false)
  427 + @Getter
  428 + private CassandraBufferedRateWriteExecutor cassandraBufferedRateWriteExecutor;
425 429
426 430 @Autowired(required = false)
427 431 @Getter
... ...
... ... @@ -557,8 +557,13 @@ class DefaultTbContext implements TbContext {
557 557 }
558 558
559 559 @Override
560   - public TbResultSetFuture submitCassandraTask(CassandraStatementTask task) {
561   - return mainCtx.getCassandraBufferedRateExecutor().submit(task);
  560 + public TbResultSetFuture submitCassandraReadTask(CassandraStatementTask task) {
  561 + return mainCtx.getCassandraBufferedRateReadExecutor().submit(task);
  562 + }
  563 +
  564 + @Override
  565 + public TbResultSetFuture submitCassandraWriteTask(CassandraStatementTask task) {
  566 + return mainCtx.getCassandraBufferedRateWriteExecutor().submit(task);
562 567 }
563 568
564 569 @Override
... ...
... ... @@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
26 26 import org.thingsboard.server.common.data.id.TenantId;
27 27 import org.thingsboard.server.dao.cassandra.CassandraCluster;
28 28 import org.thingsboard.server.dao.cassandra.guava.GuavaSession;
  29 +import org.thingsboard.server.dao.util.BufferedRateExecutor;
29 30
30 31 import java.util.concurrent.ConcurrentHashMap;
31 32 import java.util.concurrent.ConcurrentMap;
... ... @@ -40,7 +41,10 @@ public abstract class CassandraAbstractDao {
40 41 private ConcurrentMap<String, PreparedStatement> preparedStatementMap = new ConcurrentHashMap<>();
41 42
42 43 @Autowired
43   - private CassandraBufferedRateExecutor rateLimiter;
  44 + private CassandraBufferedRateReadExecutor rateReadLimiter;
  45 +
  46 + @Autowired
  47 + private CassandraBufferedRateWriteExecutor rateWriteLimiter;
44 48
45 49 private GuavaSession session;
46 50
... ... @@ -61,36 +65,38 @@ public abstract class CassandraAbstractDao {
61 65 }
62 66
63 67 protected AsyncResultSet executeRead(TenantId tenantId, Statement statement) {
64   - return execute(tenantId, statement, defaultReadLevel);
  68 + return execute(tenantId, statement, defaultReadLevel, rateReadLimiter);
65 69 }
66 70
67 71 protected AsyncResultSet executeWrite(TenantId tenantId, Statement statement) {
68   - return execute(tenantId, statement, defaultWriteLevel);
  72 + return execute(tenantId, statement, defaultWriteLevel, rateWriteLimiter);
69 73 }
70 74
71 75 protected TbResultSetFuture executeAsyncRead(TenantId tenantId, Statement statement) {
72   - return executeAsync(tenantId, statement, defaultReadLevel);
  76 + return executeAsync(tenantId, statement, defaultReadLevel, rateReadLimiter);
73 77 }
74 78
75 79 protected TbResultSetFuture executeAsyncWrite(TenantId tenantId, Statement statement) {
76   - return executeAsync(tenantId, statement, defaultWriteLevel);
  80 + return executeAsync(tenantId, statement, defaultWriteLevel, rateWriteLimiter);
77 81 }
78 82
79   - private AsyncResultSet execute(TenantId tenantId, Statement statement, ConsistencyLevel level) {
  83 + private AsyncResultSet execute(TenantId tenantId, Statement statement, ConsistencyLevel level,
  84 + BufferedRateExecutor<CassandraStatementTask, TbResultSetFuture> rateExecutor) {
80 85 if (log.isDebugEnabled()) {
81 86 log.debug("Execute cassandra statement {}", statementToString(statement));
82 87 }
83   - return executeAsync(tenantId, statement, level).getUninterruptibly();
  88 + return executeAsync(tenantId, statement, level, rateExecutor).getUninterruptibly();
84 89 }
85 90
86   - private TbResultSetFuture executeAsync(TenantId tenantId, Statement statement, ConsistencyLevel level) {
  91 + private TbResultSetFuture executeAsync(TenantId tenantId, Statement statement, ConsistencyLevel level,
  92 + BufferedRateExecutor<CassandraStatementTask, TbResultSetFuture> rateExecutor) {
87 93 if (log.isDebugEnabled()) {
88 94 log.debug("Execute cassandra async statement {}", statementToString(statement));
89 95 }
90 96 if (statement.getConsistencyLevel() == null) {
91 97 statement.setConsistencyLevel(level);
92 98 }
93   - return rateLimiter.submit(new CassandraStatementTask(tenantId, getSession(), statement));
  99 + return rateExecutor.submit(new CassandraStatementTask(tenantId, getSession(), statement));
94 100 }
95 101
96 102 private static String statementToString(Statement statement) {
... ...
dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateReadExecutor.java renamed from dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraBufferedRateExecutor.java
... ... @@ -22,9 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired;
22 22 import org.springframework.beans.factory.annotation.Value;
23 23 import org.springframework.scheduling.annotation.Scheduled;
24 24 import org.springframework.stereotype.Component;
25   -import org.thingsboard.server.common.data.id.TenantId;
26   -import org.thingsboard.server.common.stats.DefaultCounter;
27   -import org.thingsboard.server.common.stats.StatsCounter;
28 25 import org.thingsboard.server.common.stats.StatsFactory;
29 26 import org.thingsboard.server.dao.entity.EntityService;
30 27 import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor;
... ... @@ -32,8 +29,6 @@ import org.thingsboard.server.dao.util.AsyncTaskContext;
32 29 import org.thingsboard.server.dao.util.NoSqlAnyDao;
33 30
34 31 import javax.annotation.PreDestroy;
35   -import java.util.HashMap;
36   -import java.util.Map;
37 32
38 33 /**
39 34 * Created by ashvayka on 24.10.18.
... ... @@ -41,15 +36,11 @@ import java.util.Map;
41 36 @Component
42 37 @Slf4j
43 38 @NoSqlAnyDao
44   -public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor<CassandraStatementTask, TbResultSetFuture, TbResultSet> {
  39 +public class CassandraBufferedRateReadExecutor extends AbstractBufferedRateExecutor<CassandraStatementTask, TbResultSetFuture, TbResultSet> {
45 40
46   - @Autowired
47   - private EntityService entityService;
48   - private Map<TenantId, String> tenantNamesCache = new HashMap<>();
  41 + static final String BUFFER_NAME = "Read";
49 42
50   - private boolean printTenantNames;
51   -
52   - public CassandraBufferedRateExecutor(
  43 + public CassandraBufferedRateReadExecutor(
53 44 @Value("${cassandra.query.buffer_size}") int queueLimit,
54 45 @Value("${cassandra.query.concurrent_limit}") int concurrencyLimit,
55 46 @Value("${cassandra.query.permit_max_wait_time}") long maxWaitTime,
... ... @@ -60,57 +51,16 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor<
60 51 @Value("${cassandra.query.tenant_rate_limits.configuration}") String tenantRateLimitsConfiguration,
61 52 @Value("${cassandra.query.tenant_rate_limits.print_tenant_names}") boolean printTenantNames,
62 53 @Value("${cassandra.query.print_queries_freq:0}") int printQueriesFreq,
63   - @Autowired StatsFactory statsFactory) {
64   - super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, tenantRateLimitsEnabled, tenantRateLimitsConfiguration, printQueriesFreq, statsFactory);
65   - this.printTenantNames = printTenantNames;
  54 + @Autowired StatsFactory statsFactory,
  55 + @Autowired EntityService entityService) {
  56 + super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, tenantRateLimitsEnabled, tenantRateLimitsConfiguration, printQueriesFreq, statsFactory,
  57 + entityService, printTenantNames);
66 58 }
67 59
68 60 @Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}")
  61 + @Override
69 62 public void printStats() {
70   - int queueSize = getQueueSize();
71   - int rateLimitedTenantsCount = (int) stats.getRateLimitedTenants().values().stream()
72   - .filter(defaultCounter -> defaultCounter.get() > 0)
73   - .count();
74   -
75   - if (queueSize > 0
76   - || rateLimitedTenantsCount > 0
77   - || concurrencyLevel.get() > 0
78   - || stats.getStatsCounters().stream().anyMatch(counter -> counter.get() > 0)
79   - ) {
80   - StringBuilder statsBuilder = new StringBuilder();
81   -
82   - statsBuilder.append("queueSize").append(" = [").append(queueSize).append("] ");
83   - stats.getStatsCounters().forEach(counter -> {
84   - statsBuilder.append(counter.getName()).append(" = [").append(counter.get()).append("] ");
85   - });
86   - statsBuilder.append("totalRateLimitedTenants").append(" = [").append(rateLimitedTenantsCount).append("] ");
87   - statsBuilder.append(CONCURRENCY_LEVEL).append(" = [").append(concurrencyLevel.get()).append("] ");
88   -
89   - stats.getStatsCounters().forEach(StatsCounter::clear);
90   - log.info("Permits {}", statsBuilder);
91   - }
92   -
93   - stats.getRateLimitedTenants().entrySet().stream()
94   - .filter(entry -> entry.getValue().get() > 0)
95   - .forEach(entry -> {
96   - TenantId tenantId = entry.getKey();
97   - DefaultCounter counter = entry.getValue();
98   - int rateLimitedRequests = counter.get();
99   - counter.clear();
100   - if (printTenantNames) {
101   - String name = tenantNamesCache.computeIfAbsent(tenantId, tId -> {
102   - try {
103   - return entityService.fetchEntityNameAsync(TenantId.SYS_TENANT_ID, tenantId).get();
104   - } catch (Exception e) {
105   - log.error("[{}] Failed to get tenant name", tenantId, e);
106   - return "N/A";
107   - }
108   - });
109   - log.info("[{}][{}] Rate limited requests: {}", tenantId, name, rateLimitedRequests);
110   - } else {
111   - log.info("[{}] Rate limited requests: {}", tenantId, rateLimitedRequests);
112   - }
113   - });
  63 + super.printStats();
114 64 }
115 65
116 66 @PreDestroy
... ... @@ -119,6 +69,11 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor<
119 69 }
120 70
121 71 @Override
  72 + public String getBufferName() {
  73 + return BUFFER_NAME;
  74 + }
  75 +
  76 + @Override
122 77 protected SettableFuture<TbResultSet> create() {
123 78 return SettableFuture.create();
124 79 }
... ... @@ -133,7 +88,7 @@ public class CassandraBufferedRateExecutor extends AbstractBufferedRateExecutor<
133 88 CassandraStatementTask task = taskCtx.getTask();
134 89 return task.executeAsync(
135 90 statement ->
136   - this.submit(new CassandraStatementTask(task.getTenantId(), task.getSession(), statement))
  91 + this.submit(new CassandraStatementTask(task.getTenantId(), task.getSession(), statement))
137 92 );
138 93 }
139 94
... ...
  1 +/**
  2 + * Copyright © 2016-2021 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.nosql;
  17 +
  18 +import com.google.common.util.concurrent.ListenableFuture;
  19 +import com.google.common.util.concurrent.SettableFuture;
  20 +import lombok.extern.slf4j.Slf4j;
  21 +import org.springframework.beans.factory.annotation.Autowired;
  22 +import org.springframework.beans.factory.annotation.Value;
  23 +import org.springframework.scheduling.annotation.Scheduled;
  24 +import org.springframework.stereotype.Component;
  25 +import org.thingsboard.server.common.stats.StatsFactory;
  26 +import org.thingsboard.server.dao.entity.EntityService;
  27 +import org.thingsboard.server.dao.util.AbstractBufferedRateExecutor;
  28 +import org.thingsboard.server.dao.util.AsyncTaskContext;
  29 +import org.thingsboard.server.dao.util.NoSqlAnyDao;
  30 +
  31 +import javax.annotation.PreDestroy;
  32 +
  33 +/**
  34 + * Created by ashvayka on 24.10.18.
  35 + */
  36 +@Component
  37 +@Slf4j
  38 +@NoSqlAnyDao
  39 +public class CassandraBufferedRateWriteExecutor extends AbstractBufferedRateExecutor<CassandraStatementTask, TbResultSetFuture, TbResultSet> {
  40 +
  41 + static final String BUFFER_NAME = "Write";
  42 +
  43 + public CassandraBufferedRateWriteExecutor(
  44 + @Value("${cassandra.query.buffer_size}") int queueLimit,
  45 + @Value("${cassandra.query.concurrent_limit}") int concurrencyLimit,
  46 + @Value("${cassandra.query.permit_max_wait_time}") long maxWaitTime,
  47 + @Value("${cassandra.query.dispatcher_threads:2}") int dispatcherThreads,
  48 + @Value("${cassandra.query.callback_threads:4}") int callbackThreads,
  49 + @Value("${cassandra.query.poll_ms:50}") long pollMs,
  50 + @Value("${cassandra.query.tenant_rate_limits.enabled}") boolean tenantRateLimitsEnabled,
  51 + @Value("${cassandra.query.tenant_rate_limits.configuration}") String tenantRateLimitsConfiguration,
  52 + @Value("${cassandra.query.tenant_rate_limits.print_tenant_names}") boolean printTenantNames,
  53 + @Value("${cassandra.query.print_queries_freq:0}") int printQueriesFreq,
  54 + @Autowired StatsFactory statsFactory,
  55 + @Autowired EntityService entityService) {
  56 + super(queueLimit, concurrencyLimit, maxWaitTime, dispatcherThreads, callbackThreads, pollMs, tenantRateLimitsEnabled, tenantRateLimitsConfiguration, printQueriesFreq, statsFactory,
  57 + entityService, printTenantNames);
  58 + }
  59 +
  60 + @Scheduled(fixedDelayString = "${cassandra.query.rate_limit_print_interval_ms}")
  61 + @Override
  62 + public void printStats() {
  63 + super.printStats();
  64 + }
  65 +
  66 + @PreDestroy
  67 + public void stop() {
  68 + super.stop();
  69 + }
  70 +
  71 + @Override
  72 + public String getBufferName() {
  73 + return BUFFER_NAME;
  74 + }
  75 +
  76 + @Override
  77 + protected SettableFuture<TbResultSet> create() {
  78 + return SettableFuture.create();
  79 + }
  80 +
  81 + @Override
  82 + protected TbResultSetFuture wrap(CassandraStatementTask task, SettableFuture<TbResultSet> future) {
  83 + return new TbResultSetFuture(future);
  84 + }
  85 +
  86 + @Override
  87 + protected ListenableFuture<TbResultSet> execute(AsyncTaskContext<CassandraStatementTask, TbResultSet> taskCtx) {
  88 + CassandraStatementTask task = taskCtx.getTask();
  89 + return task.executeAsync(
  90 + statement ->
  91 + this.submit(new CassandraStatementTask(task.getTenantId(), task.getSession(), statement))
  92 + );
  93 + }
  94 +
  95 +}
... ...
... ... @@ -31,12 +31,17 @@ import lombok.extern.slf4j.Slf4j;
31 31 import org.thingsboard.common.util.ThingsBoardExecutors;
32 32 import org.thingsboard.common.util.ThingsBoardThreadFactory;
33 33 import org.thingsboard.server.common.data.id.TenantId;
  34 +import org.thingsboard.server.common.msg.tools.TbRateLimits;
  35 +import org.thingsboard.server.common.stats.DefaultCounter;
  36 +import org.thingsboard.server.common.stats.StatsCounter;
34 37 import org.thingsboard.server.common.stats.StatsFactory;
35 38 import org.thingsboard.server.common.stats.StatsType;
36   -import org.thingsboard.server.common.msg.tools.TbRateLimits;
  39 +import org.thingsboard.server.dao.entity.EntityService;
37 40 import org.thingsboard.server.dao.nosql.CassandraStatementTask;
38 41
39 42 import javax.annotation.Nullable;
  43 +import java.util.HashMap;
  44 +import java.util.Map;
40 45 import java.util.UUID;
41 46 import java.util.concurrent.BlockingQueue;
42 47 import java.util.concurrent.ConcurrentHashMap;
... ... @@ -75,22 +80,32 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
75 80 protected final AtomicInteger concurrencyLevel;
76 81 protected final BufferedRateExecutorStats stats;
77 82
  83 +
  84 + private final EntityService entityService;
  85 + private final Map<TenantId, String> tenantNamesCache = new HashMap<>();
  86 +
  87 + private final boolean printTenantNames;
  88 +
78 89 public AbstractBufferedRateExecutor(int queueLimit, int concurrencyLimit, long maxWaitTime, int dispatcherThreads, int callbackThreads, long pollMs,
79   - boolean perTenantLimitsEnabled, String perTenantLimitsConfiguration, int printQueriesFreq, StatsFactory statsFactory) {
  90 + boolean perTenantLimitsEnabled, String perTenantLimitsConfiguration, int printQueriesFreq, StatsFactory statsFactory,
  91 + EntityService entityService, boolean printTenantNames) {
80 92 this.maxWaitTime = maxWaitTime;
81 93 this.pollMs = pollMs;
82 94 this.concurrencyLimit = concurrencyLimit;
83 95 this.printQueriesFreq = printQueriesFreq;
84 96 this.queue = new LinkedBlockingDeque<>(queueLimit);
85   - this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads, ThingsBoardThreadFactory.forName("nosql-dispatcher"));
86   - this.callbackExecutor = ThingsBoardExecutors.newWorkStealingPool(callbackThreads, getClass());
87   - this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("nosql-timeout"));
  97 + this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads, ThingsBoardThreadFactory.forName("nosql-" + getBufferName() + "-dispatcher"));
  98 + this.callbackExecutor = ThingsBoardExecutors.newWorkStealingPool(callbackThreads, "nosql-" + getBufferName() + "-callback");
  99 + this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("nosql-" + getBufferName() + "-timeout"));
88 100 this.perTenantLimitsEnabled = perTenantLimitsEnabled;
89 101 this.perTenantLimitsConfiguration = perTenantLimitsConfiguration;
90 102 this.stats = new BufferedRateExecutorStats(statsFactory);
91   - String concurrencyLevelKey = StatsType.RATE_EXECUTOR.getName() + "." + CONCURRENCY_LEVEL;
  103 + String concurrencyLevelKey = StatsType.RATE_EXECUTOR.getName() + "." + CONCURRENCY_LEVEL + getBufferName(); //metric name may change with buffer name suffix
92 104 this.concurrencyLevel = statsFactory.createGauge(concurrencyLevelKey, new AtomicInteger(0));
93 105
  106 + this.entityService = entityService;
  107 + this.printTenantNames = printTenantNames;
  108 +
94 109 for (int i = 0; i < dispatcherThreads; i++) {
95 110 dispatcherExecutor.submit(this::dispatch);
96 111 }
... ... @@ -144,6 +159,8 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
144 159
145 160 protected abstract ListenableFuture<V> execute(AsyncTaskContext<T, V> taskCtx);
146 161
  162 + public abstract String getBufferName();
  163 +
147 164 private void dispatch() {
148 165 log.info("Buffered rate executor thread started");
149 166 while (!Thread.interrupted()) {
... ... @@ -264,4 +281,51 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
264 281 protected int getQueueSize() {
265 282 return queue.size();
266 283 }
  284 +
  285 + public void printStats() {
  286 + int queueSize = getQueueSize();
  287 + int rateLimitedTenantsCount = (int) stats.getRateLimitedTenants().values().stream()
  288 + .filter(defaultCounter -> defaultCounter.get() > 0)
  289 + .count();
  290 +
  291 + if (queueSize > 0
  292 + || rateLimitedTenantsCount > 0
  293 + || concurrencyLevel.get() > 0
  294 + || stats.getStatsCounters().stream().anyMatch(counter -> counter.get() > 0)
  295 + ) {
  296 + StringBuilder statsBuilder = new StringBuilder();
  297 +
  298 + statsBuilder.append("queueSize").append(" = [").append(queueSize).append("] ");
  299 + stats.getStatsCounters().forEach(counter -> {
  300 + statsBuilder.append(counter.getName()).append(" = [").append(counter.get()).append("] ");
  301 + });
  302 + statsBuilder.append("totalRateLimitedTenants").append(" = [").append(rateLimitedTenantsCount).append("] ");
  303 + statsBuilder.append(CONCURRENCY_LEVEL).append(" = [").append(concurrencyLevel.get()).append("] ");
  304 +
  305 + stats.getStatsCounters().forEach(StatsCounter::clear);
  306 + log.info("Permits {}", statsBuilder);
  307 + }
  308 +
  309 + stats.getRateLimitedTenants().entrySet().stream()
  310 + .filter(entry -> entry.getValue().get() > 0)
  311 + .forEach(entry -> {
  312 + TenantId tenantId = entry.getKey();
  313 + DefaultCounter counter = entry.getValue();
  314 + int rateLimitedRequests = counter.get();
  315 + counter.clear();
  316 + if (printTenantNames) {
  317 + String name = tenantNamesCache.computeIfAbsent(tenantId, tId -> {
  318 + try {
  319 + return entityService.fetchEntityNameAsync(TenantId.SYS_TENANT_ID, tenantId).get();
  320 + } catch (Exception e) {
  321 + log.error("[{}] Failed to get tenant name", tenantId, e);
  322 + return "N/A";
  323 + }
  324 + });
  325 + log.info("[{}][{}] Rate limited requests: {}", tenantId, name, rateLimitedRequests);
  326 + } else {
  327 + log.info("[{}] Rate limited requests: {}", tenantId, rateLimitedRequests);
  328 + }
  329 + });
  330 + }
267 331 }
... ...
... ... @@ -20,6 +20,7 @@ import com.datastax.oss.driver.api.core.cql.BoundStatement;
20 20 import com.datastax.oss.driver.api.core.cql.PreparedStatement;
21 21 import com.datastax.oss.driver.api.core.cql.Statement;
22 22 import com.google.common.util.concurrent.Futures;
  23 +import com.google.common.util.concurrent.SettableFuture;
23 24 import org.junit.Before;
24 25 import org.junit.Test;
25 26 import org.junit.runner.RunWith;
... ... @@ -38,6 +39,7 @@ import java.util.UUID;
38 39 import static org.mockito.ArgumentMatchers.any;
39 40 import static org.mockito.ArgumentMatchers.anyInt;
40 41 import static org.mockito.ArgumentMatchers.anyString;
  42 +import static org.mockito.BDDMockito.willReturn;
41 43 import static org.mockito.Mockito.doReturn;
42 44 import static org.mockito.Mockito.times;
43 45 import static org.mockito.Mockito.verify;
... ... @@ -59,9 +61,6 @@ public class CassandraPartitionsCacheTest {
59 61 private Environment environment;
60 62
61 63 @Mock
62   - private CassandraBufferedRateExecutor rateLimiter;
63   -
64   - @Mock
65 64 private CassandraCluster cluster;
66 65
67 66 @Mock
... ... @@ -74,7 +73,6 @@ public class CassandraPartitionsCacheTest {
74 73 ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "systemTtl", 0);
75 74 ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "setNullValuesEnabled", false);
76 75 ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "environment", environment);
77   - ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "rateLimiter", rateLimiter);
78 76 ReflectionTestUtils.setField(cassandraBaseTimeseriesDao, "cluster", cluster);
79 77
80 78 when(cluster.getDefaultReadConsistencyLevel()).thenReturn(ConsistencyLevel.ONE);
... ... @@ -88,7 +86,9 @@ public class CassandraPartitionsCacheTest {
88 86 when(boundStatement.setUuid(anyInt(), any(UUID.class))).thenReturn(boundStatement);
89 87 when(boundStatement.setLong(anyInt(), any(Long.class))).thenReturn(boundStatement);
90 88
91   - doReturn(Futures.immediateFuture(0)).when(cassandraBaseTimeseriesDao).getFuture(any(TbResultSetFuture.class), any());
  89 + willReturn(new TbResultSetFuture(SettableFuture.create())).given(cassandraBaseTimeseriesDao).executeAsyncWrite(any(), any());
  90 +
  91 + doReturn(Futures.immediateFuture(0)).when(cassandraBaseTimeseriesDao).getFuture(any(), any());
92 92 }
93 93
94 94 @Test
... ... @@ -107,4 +107,5 @@ public class CassandraPartitionsCacheTest {
107 107 }
108 108 verify(cassandraBaseTimeseriesDao, times(60000)).executeAsyncWrite(any(TenantId.class), any(Statement.class));
109 109 }
  110 +
110 111 }
... ...
... ... @@ -245,7 +245,9 @@ public interface TbContext {
245 245
246 246 CassandraCluster getCassandraCluster();
247 247
248   - TbResultSetFuture submitCassandraTask(CassandraStatementTask task);
  248 + TbResultSetFuture submitCassandraReadTask(CassandraStatementTask task);
  249 +
  250 + TbResultSetFuture submitCassandraWriteTask(CassandraStatementTask task);
249 251
250 252 PageData<RuleNodeState> findRuleNodeStates(PageLink pageLink);
251 253
... ...
... ... @@ -213,7 +213,7 @@ public class TbSaveToCustomCassandraTableNode implements TbNode {
213 213 if (statement.getConsistencyLevel() == null) {
214 214 statement.setConsistencyLevel(level);
215 215 }
216   - return ctx.submitCassandraTask(new CassandraStatementTask(ctx.getTenantId(), getSession(), statement));
  216 + return ctx.submitCassandraWriteTask(new CassandraStatementTask(ctx.getTenantId(), getSession(), statement));
217 217 }
218 218
219 219 private static String statementToString(Statement statement) {
... ...