Showing
37 changed files
with
492 additions
and
179 deletions
... | ... | @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Value; |
23 | 23 | import org.springframework.stereotype.Service; |
24 | 24 | import org.thingsboard.server.common.data.id.TenantId; |
25 | 25 | import org.thingsboard.server.common.msg.TbMsg; |
26 | +import org.thingsboard.server.common.transport.quota.tenant.TenantQuotaService; | |
26 | 27 | import org.thingsboard.server.dao.queue.MsgQueue; |
27 | 28 | |
28 | 29 | import javax.annotation.PostConstruct; |
... | ... | @@ -48,6 +49,9 @@ public class DefaultMsgQueueService implements MsgQueueService { |
48 | 49 | @Autowired |
49 | 50 | private MsgQueue msgQueue; |
50 | 51 | |
52 | + @Autowired | |
53 | + private TenantQuotaService quotaService; | |
54 | + | |
51 | 55 | private ScheduledExecutorService cleanupExecutor; |
52 | 56 | |
53 | 57 | private Map<TenantId, AtomicLong> pendingCountPerTenant = new ConcurrentHashMap<>(); |
... | ... | @@ -70,6 +74,11 @@ public class DefaultMsgQueueService implements MsgQueueService { |
70 | 74 | |
71 | 75 | @Override |
72 | 76 | public ListenableFuture<Void> put(TenantId tenantId, TbMsg msg, UUID nodeId, long clusterPartition) { |
77 | + if(quotaService.isQuotaExceeded(tenantId.getId().toString())) { | |
78 | + log.warn("Tenant TbMsg Quota exceeded for [{}:{}] . Reject", tenantId.getId()); | |
79 | + return Futures.immediateFailedFuture(new RuntimeException("Tenant TbMsg Quota exceeded")); | |
80 | + } | |
81 | + | |
73 | 82 | AtomicLong pendingMsgCount = pendingCountPerTenant.computeIfAbsent(tenantId, key -> new AtomicLong()); |
74 | 83 | if (pendingMsgCount.incrementAndGet() < queueMaxSize) { |
75 | 84 | return msgQueue.put(tenantId, msg, nodeId, clusterPartition); | ... | ... |
... | ... | @@ -131,9 +131,28 @@ quota: |
131 | 131 | whitelist: "${QUOTA_HOST_WHITELIST:localhost,127.0.0.1}" |
132 | 132 | # Array of blacklist hosts |
133 | 133 | blacklist: "${QUOTA_HOST_BLACKLIST:}" |
134 | - log: | |
135 | - topSize: 10 | |
136 | - intervalMin: 2 | |
134 | + log: | |
135 | + topSize: 10 | |
136 | + intervalMin: 2 | |
137 | + rule: | |
138 | + tenant: | |
139 | + # Max allowed number of API requests in interval for single tenant | |
140 | + limit: "${QUOTA_TENANT_LIMIT:100000}" | |
141 | + # Interval duration | |
142 | + intervalMs: "${QUOTA_TENANT_INTERVAL_MS:60000}" | |
143 | + # Maximum silence duration for tenant after which Tenant removed from QuotaService. Must be bigger than intervalMs | |
144 | + ttlMs: "${QUOTA_TENANT_TTL_MS:60000}" | |
145 | + # Interval for scheduled task that cleans expired records. TTL is used for expiring | |
146 | + cleanPeriodMs: "${QUOTA_TENANT_CLEAN_PERIOD_MS:300000}" | |
147 | + # Enable Host API Limits | |
148 | + enabled: "${QUOTA_TENANT_ENABLED:false}" | |
149 | + # Array of whitelist tenants | |
150 | + whitelist: "${QUOTA_TENANT_WHITELIST:}" | |
151 | + # Array of blacklist tenants | |
152 | + blacklist: "${QUOTA_HOST_BLACKLIST:}" | |
153 | + log: | |
154 | + topSize: 10 | |
155 | + intervalMin: 2 | |
137 | 156 | |
138 | 157 | database: |
139 | 158 | type: "${DATABASE_TYPE:sql}" # cassandra OR sql | ... | ... |
common/transport/src/main/java/org/thingsboard/server/common/transport/quota/AbstractQuotaService.java
renamed from
common/transport/src/main/java/org/thingsboard/server/common/transport/quota/HostRequestsQuotaService.java
... | ... | @@ -15,33 +15,24 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.transport.quota; |
17 | 17 | |
18 | -import lombok.extern.slf4j.Slf4j; | |
19 | -import org.springframework.beans.factory.annotation.Value; | |
20 | -import org.springframework.stereotype.Service; | |
21 | -import org.thingsboard.server.common.transport.quota.inmemory.HostRequestIntervalRegistry; | |
22 | 18 | import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; |
23 | 19 | import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; |
20 | +import org.thingsboard.server.common.transport.quota.inmemory.KeyBasedIntervalRegistry; | |
24 | 21 | |
25 | 22 | import javax.annotation.PostConstruct; |
26 | 23 | import javax.annotation.PreDestroy; |
27 | 24 | |
28 | -/** | |
29 | - * @author Vitaliy Paromskiy | |
30 | - * @version 1.0 | |
31 | - */ | |
32 | -@Service | |
33 | -@Slf4j | |
34 | -public class HostRequestsQuotaService implements QuotaService { | |
25 | +public class AbstractQuotaService implements QuotaService { | |
35 | 26 | |
36 | - private final HostRequestIntervalRegistry requestRegistry; | |
37 | - private final HostRequestLimitPolicy requestsPolicy; | |
27 | + private final KeyBasedIntervalRegistry requestRegistry; | |
28 | + private final RequestLimitPolicy requestsPolicy; | |
38 | 29 | private final IntervalRegistryCleaner registryCleaner; |
39 | 30 | private final IntervalRegistryLogger registryLogger; |
40 | 31 | private final boolean enabled; |
41 | 32 | |
42 | - public HostRequestsQuotaService(HostRequestIntervalRegistry requestRegistry, HostRequestLimitPolicy requestsPolicy, | |
33 | + public AbstractQuotaService(KeyBasedIntervalRegistry requestRegistry, RequestLimitPolicy requestsPolicy, | |
43 | 34 | IntervalRegistryCleaner registryCleaner, IntervalRegistryLogger registryLogger, |
44 | - @Value("${quota.host.enabled}") boolean enabled) { | |
35 | + boolean enabled) { | |
45 | 36 | this.requestRegistry = requestRegistry; |
46 | 37 | this.requestsPolicy = requestsPolicy; |
47 | 38 | this.registryCleaner = registryCleaner; | ... | ... |
common/transport/src/main/java/org/thingsboard/server/common/transport/quota/RequestLimitPolicy.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2018 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.common.transport.quota; | |
17 | + | |
18 | + | |
19 | +public abstract class RequestLimitPolicy { | |
20 | + | |
21 | + private final long limit; | |
22 | + | |
23 | + public RequestLimitPolicy(long limit) { | |
24 | + this.limit = limit; | |
25 | + } | |
26 | + | |
27 | + public boolean isValid(long currentValue) { | |
28 | + return currentValue <= limit; | |
29 | + } | |
30 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 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.common.transport.quota.host; | |
17 | + | |
18 | +import org.springframework.beans.factory.annotation.Value; | |
19 | +import org.springframework.stereotype.Component; | |
20 | +import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; | |
21 | + | |
22 | +@Component | |
23 | +public class HostIntervalRegistryCleaner extends IntervalRegistryCleaner { | |
24 | + | |
25 | + public HostIntervalRegistryCleaner(HostRequestIntervalRegistry intervalRegistry, | |
26 | + @Value("${quota.host.cleanPeriodMs}") long cleanPeriodMs) { | |
27 | + super(intervalRegistry, cleanPeriodMs); | |
28 | + } | |
29 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 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.common.transport.quota.host; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.springframework.beans.factory.annotation.Value; | |
20 | +import org.springframework.stereotype.Component; | |
21 | +import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; | |
22 | + | |
23 | +import java.util.Map; | |
24 | +import java.util.concurrent.TimeUnit; | |
25 | + | |
26 | +@Component | |
27 | +@Slf4j | |
28 | +public class HostIntervalRegistryLogger extends IntervalRegistryLogger { | |
29 | + | |
30 | + private final long logIntervalMin; | |
31 | + | |
32 | + public HostIntervalRegistryLogger(@Value("${quota.host.log.topSize}") int topSize, | |
33 | + @Value("${quota.host.log.intervalMin}") long logIntervalMin, | |
34 | + HostRequestIntervalRegistry intervalRegistry) { | |
35 | + super(topSize, logIntervalMin, intervalRegistry); | |
36 | + this.logIntervalMin = logIntervalMin; | |
37 | + } | |
38 | + | |
39 | + protected void log(Map<String, Long> top, int uniqHosts, long requestsCount) { | |
40 | + long rps = requestsCount / TimeUnit.MINUTES.toSeconds(logIntervalMin); | |
41 | + StringBuilder builder = new StringBuilder("Quota Statistic : "); | |
42 | + builder.append("uniqHosts : ").append(uniqHosts).append("; "); | |
43 | + builder.append("requestsCount : ").append(requestsCount).append("; "); | |
44 | + builder.append("RPS : ").append(rps).append(" "); | |
45 | + builder.append("top -> "); | |
46 | + for (Map.Entry<String, Long> host : top.entrySet()) { | |
47 | + builder.append(host.getKey()).append(" : ").append(host.getValue()).append("; "); | |
48 | + } | |
49 | + | |
50 | + log.info(builder.toString()); | |
51 | + } | |
52 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 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.common.transport.quota.host; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.springframework.beans.factory.annotation.Value; | |
20 | +import org.springframework.stereotype.Component; | |
21 | +import org.thingsboard.server.common.transport.quota.inmemory.KeyBasedIntervalRegistry; | |
22 | + | |
23 | +/** | |
24 | + * @author Vitaliy Paromskiy | |
25 | + * @version 1.0 | |
26 | + */ | |
27 | +@Component | |
28 | +@Slf4j | |
29 | +public class HostRequestIntervalRegistry extends KeyBasedIntervalRegistry { | |
30 | + | |
31 | + public HostRequestIntervalRegistry(@Value("${quota.host.intervalMs}") long intervalDurationMs, | |
32 | + @Value("${quota.host.ttlMs}") long ttlMs, | |
33 | + @Value("${quota.host.whitelist}") String whiteList, | |
34 | + @Value("${quota.host.blacklist}") String blackList) { | |
35 | + super(intervalDurationMs, ttlMs, whiteList, blackList, "host"); | |
36 | + } | |
37 | +} | ... | ... |
common/transport/src/main/java/org/thingsboard/server/common/transport/quota/host/HostRequestLimitPolicy.java
renamed from
common/transport/src/main/java/org/thingsboard/server/common/transport/quota/HostRequestLimitPolicy.java
... | ... | @@ -13,26 +13,21 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.common.transport.quota; | |
16 | +package org.thingsboard.server.common.transport.quota.host; | |
17 | 17 | |
18 | 18 | import org.springframework.beans.factory.annotation.Value; |
19 | 19 | import org.springframework.stereotype.Component; |
20 | +import org.thingsboard.server.common.transport.quota.RequestLimitPolicy; | |
20 | 21 | |
21 | 22 | /** |
22 | 23 | * @author Vitaliy Paromskiy |
23 | 24 | * @version 1.0 |
24 | 25 | */ |
25 | 26 | @Component |
26 | -public class HostRequestLimitPolicy { | |
27 | - | |
28 | - private final long limit; | |
27 | +public class HostRequestLimitPolicy extends RequestLimitPolicy { | |
29 | 28 | |
30 | 29 | public HostRequestLimitPolicy(@Value("${quota.host.limit}") long limit) { |
31 | - this.limit = limit; | |
32 | - } | |
33 | - | |
34 | - public boolean isValid(long currentValue) { | |
35 | - return currentValue <= limit; | |
30 | + super(limit); | |
36 | 31 | } |
37 | 32 | |
38 | 33 | } | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 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.common.transport.quota.host; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.springframework.beans.factory.annotation.Value; | |
20 | +import org.springframework.stereotype.Service; | |
21 | +import org.thingsboard.server.common.transport.quota.AbstractQuotaService; | |
22 | + | |
23 | +/** | |
24 | + * @author Vitaliy Paromskiy | |
25 | + * @version 1.0 | |
26 | + */ | |
27 | +@Service | |
28 | +@Slf4j | |
29 | +public class HostRequestsQuotaService extends AbstractQuotaService { | |
30 | + | |
31 | + public HostRequestsQuotaService(HostRequestIntervalRegistry requestRegistry, HostRequestLimitPolicy requestsPolicy, | |
32 | + HostIntervalRegistryCleaner registryCleaner, HostIntervalRegistryLogger registryLogger, | |
33 | + @Value("${quota.host.enabled}") boolean enabled) { | |
34 | + super(requestRegistry, requestsPolicy, registryCleaner, registryLogger, enabled); | |
35 | + } | |
36 | + | |
37 | +} | ... | ... |
... | ... | @@ -16,10 +16,7 @@ |
16 | 16 | package org.thingsboard.server.common.transport.quota.inmemory; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | -import org.springframework.beans.factory.annotation.Value; | |
20 | -import org.springframework.stereotype.Component; | |
21 | 19 | |
22 | -import javax.annotation.PreDestroy; | |
23 | 20 | import java.util.concurrent.Executors; |
24 | 21 | import java.util.concurrent.ScheduledExecutorService; |
25 | 22 | import java.util.concurrent.TimeUnit; |
... | ... | @@ -28,15 +25,14 @@ import java.util.concurrent.TimeUnit; |
28 | 25 | * @author Vitaliy Paromskiy |
29 | 26 | * @version 1.0 |
30 | 27 | */ |
31 | -@Component | |
32 | 28 | @Slf4j |
33 | -public class IntervalRegistryCleaner { | |
29 | +public abstract class IntervalRegistryCleaner { | |
34 | 30 | |
35 | - private final HostRequestIntervalRegistry intervalRegistry; | |
31 | + private final KeyBasedIntervalRegistry intervalRegistry; | |
36 | 32 | private final long cleanPeriodMs; |
37 | 33 | private ScheduledExecutorService executor; |
38 | 34 | |
39 | - public IntervalRegistryCleaner(HostRequestIntervalRegistry intervalRegistry, @Value("${quota.host.cleanPeriodMs}") long cleanPeriodMs) { | |
35 | + public IntervalRegistryCleaner(KeyBasedIntervalRegistry intervalRegistry, long cleanPeriodMs) { | |
40 | 36 | this.intervalRegistry = intervalRegistry; |
41 | 37 | this.cleanPeriodMs = cleanPeriodMs; |
42 | 38 | } | ... | ... |
... | ... | @@ -17,8 +17,6 @@ package org.thingsboard.server.common.transport.quota.inmemory; |
17 | 17 | |
18 | 18 | import com.google.common.collect.MinMaxPriorityQueue; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | -import org.springframework.beans.factory.annotation.Value; | |
21 | -import org.springframework.stereotype.Component; | |
22 | 20 | |
23 | 21 | import java.util.Comparator; |
24 | 22 | import java.util.Map; |
... | ... | @@ -32,17 +30,15 @@ import java.util.stream.Collectors; |
32 | 30 | * @author Vitaliy Paromskiy |
33 | 31 | * @version 1.0 |
34 | 32 | */ |
35 | -@Component | |
36 | 33 | @Slf4j |
37 | -public class IntervalRegistryLogger { | |
34 | +public abstract class IntervalRegistryLogger { | |
38 | 35 | |
39 | 36 | private final int topSize; |
40 | - private final HostRequestIntervalRegistry intervalRegistry; | |
37 | + private final KeyBasedIntervalRegistry intervalRegistry; | |
41 | 38 | private final long logIntervalMin; |
42 | 39 | private ScheduledExecutorService executor; |
43 | 40 | |
44 | - public IntervalRegistryLogger(@Value("${quota.log.topSize}") int topSize, @Value("${quota.log.intervalMin}") long logIntervalMin, | |
45 | - HostRequestIntervalRegistry intervalRegistry) { | |
41 | + public IntervalRegistryLogger(int topSize, long logIntervalMin, KeyBasedIntervalRegistry intervalRegistry) { | |
46 | 42 | this.topSize = topSize; |
47 | 43 | this.logIntervalMin = logIntervalMin; |
48 | 44 | this.intervalRegistry = intervalRegistry; |
... | ... | @@ -79,17 +75,5 @@ public class IntervalRegistryLogger { |
79 | 75 | return topQueue.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
80 | 76 | } |
81 | 77 | |
82 | - private void log(Map<String, Long> top, int uniqHosts, long requestsCount) { | |
83 | - long rps = requestsCount / TimeUnit.MINUTES.toSeconds(logIntervalMin); | |
84 | - StringBuilder builder = new StringBuilder("Quota Statistic : "); | |
85 | - builder.append("uniqHosts : ").append(uniqHosts).append("; "); | |
86 | - builder.append("requestsCount : ").append(requestsCount).append("; "); | |
87 | - builder.append("RPS : ").append(rps).append(" "); | |
88 | - builder.append("top -> "); | |
89 | - for (Map.Entry<String, Long> host : top.entrySet()) { | |
90 | - builder.append(host.getKey()).append(" : ").append(host.getValue()).append("; "); | |
91 | - } | |
92 | - | |
93 | - log.info(builder.toString()); | |
94 | - } | |
78 | + protected abstract void log(Map<String, Long> top, int uniqHosts, long requestsCount); | |
95 | 79 | } | ... | ... |
common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/KeyBasedIntervalRegistry.java
renamed from
common/transport/src/main/java/org/thingsboard/server/common/transport/quota/inmemory/HostRequestIntervalRegistry.java
... | ... | @@ -18,22 +18,14 @@ package org.thingsboard.server.common.transport.quota.inmemory; |
18 | 18 | import com.google.common.collect.Sets; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | 20 | import org.apache.commons.lang3.StringUtils; |
21 | -import org.springframework.beans.factory.annotation.Value; | |
22 | -import org.springframework.stereotype.Component; | |
23 | 21 | |
24 | -import javax.annotation.PostConstruct; | |
25 | 22 | import java.util.Map; |
26 | 23 | import java.util.Set; |
27 | 24 | import java.util.concurrent.ConcurrentHashMap; |
28 | 25 | import java.util.stream.Collectors; |
29 | 26 | |
30 | -/** | |
31 | - * @author Vitaliy Paromskiy | |
32 | - * @version 1.0 | |
33 | - */ | |
34 | -@Component | |
35 | 27 | @Slf4j |
36 | -public class HostRequestIntervalRegistry { | |
28 | +public abstract class KeyBasedIntervalRegistry { | |
37 | 29 | |
38 | 30 | private final Map<String, IntervalCount> hostCounts = new ConcurrentHashMap<>(); |
39 | 31 | private final long intervalDurationMs; |
... | ... | @@ -41,23 +33,20 @@ public class HostRequestIntervalRegistry { |
41 | 33 | private final Set<String> whiteList; |
42 | 34 | private final Set<String> blackList; |
43 | 35 | |
44 | - public HostRequestIntervalRegistry(@Value("${quota.host.intervalMs}") long intervalDurationMs, | |
45 | - @Value("${quota.host.ttlMs}") long ttlMs, | |
46 | - @Value("${quota.host.whitelist}") String whiteList, | |
47 | - @Value("${quota.host.blacklist}") String blackList) { | |
36 | + public KeyBasedIntervalRegistry(long intervalDurationMs, long ttlMs, String whiteList, String blackList, String name) { | |
48 | 37 | this.intervalDurationMs = intervalDurationMs; |
49 | 38 | this.ttlMs = ttlMs; |
50 | 39 | this.whiteList = Sets.newHashSet(StringUtils.split(whiteList, ',')); |
51 | 40 | this.blackList = Sets.newHashSet(StringUtils.split(blackList, ',')); |
41 | + validate(name); | |
52 | 42 | } |
53 | 43 | |
54 | - @PostConstruct | |
55 | - public void init() { | |
44 | + private void validate(String name) { | |
56 | 45 | if (ttlMs < intervalDurationMs) { |
57 | - log.warn("TTL for IntervalRegistry [{}] smaller than interval duration [{}]", ttlMs, intervalDurationMs); | |
46 | + log.warn("TTL for {} IntervalRegistry [{}] smaller than interval duration [{}]", name, ttlMs, intervalDurationMs); | |
58 | 47 | } |
59 | - log.info("Start Host Quota Service with whitelist {}", whiteList); | |
60 | - log.info("Start Host Quota Service with blacklist {}", blackList); | |
48 | + log.info("Start {} KeyBasedIntervalRegistry with whitelist {}", name, whiteList); | |
49 | + log.info("Start {} KeyBasedIntervalRegistry with blacklist {}", name, blackList); | |
61 | 50 | } |
62 | 51 | |
63 | 52 | public long tick(String clientHostId) { | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 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.common.transport.quota.tenant; | |
17 | + | |
18 | +import org.springframework.beans.factory.annotation.Value; | |
19 | +import org.springframework.stereotype.Component; | |
20 | +import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; | |
21 | + | |
22 | +@Component | |
23 | +public class TenantIntervalRegistryCleaner extends IntervalRegistryCleaner { | |
24 | + | |
25 | + public TenantIntervalRegistryCleaner(TenantMsgsIntervalRegistry intervalRegistry, | |
26 | + @Value("${quota.rule.tenant.cleanPeriodMs}") long cleanPeriodMs) { | |
27 | + super(intervalRegistry, cleanPeriodMs); | |
28 | + } | |
29 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 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.common.transport.quota.tenant; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.springframework.beans.factory.annotation.Value; | |
20 | +import org.springframework.stereotype.Component; | |
21 | +import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; | |
22 | + | |
23 | +import java.util.Map; | |
24 | +import java.util.concurrent.TimeUnit; | |
25 | + | |
26 | +@Slf4j | |
27 | +@Component | |
28 | +public class TenantIntervalRegistryLogger extends IntervalRegistryLogger { | |
29 | + | |
30 | + private final long logIntervalMin; | |
31 | + | |
32 | + public TenantIntervalRegistryLogger(@Value("${quota.rule.tenant.log.topSize}") int topSize, | |
33 | + @Value("${quota.rule.tenant.log.intervalMin}") long logIntervalMin, | |
34 | + TenantMsgsIntervalRegistry intervalRegistry) { | |
35 | + super(topSize, logIntervalMin, intervalRegistry); | |
36 | + this.logIntervalMin = logIntervalMin; | |
37 | + } | |
38 | + | |
39 | + protected void log(Map<String, Long> top, int uniqHosts, long requestsCount) { | |
40 | + long rps = requestsCount / TimeUnit.MINUTES.toSeconds(logIntervalMin); | |
41 | + StringBuilder builder = new StringBuilder("Tenant Quota Statistic : "); | |
42 | + builder.append("uniqTenants : ").append(uniqHosts).append("; "); | |
43 | + builder.append("requestsCount : ").append(requestsCount).append("; "); | |
44 | + builder.append("RPS : ").append(rps).append(" "); | |
45 | + builder.append("top -> "); | |
46 | + for (Map.Entry<String, Long> host : top.entrySet()) { | |
47 | + builder.append(host.getKey()).append(" : ").append(host.getValue()).append("; "); | |
48 | + } | |
49 | + | |
50 | + log.info(builder.toString()); | |
51 | + } | |
52 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 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.common.transport.quota.tenant; | |
17 | + | |
18 | +import org.springframework.beans.factory.annotation.Value; | |
19 | +import org.springframework.stereotype.Component; | |
20 | +import org.thingsboard.server.common.transport.quota.inmemory.KeyBasedIntervalRegistry; | |
21 | + | |
22 | +@Component | |
23 | +public class TenantMsgsIntervalRegistry extends KeyBasedIntervalRegistry { | |
24 | + | |
25 | + public TenantMsgsIntervalRegistry(@Value("${quota.rule.tenant.intervalMs}") long intervalDurationMs, | |
26 | + @Value("${quota.rule.tenant.ttlMs}") long ttlMs, | |
27 | + @Value("${quota.rule.tenant.whitelist}") String whiteList, | |
28 | + @Value("${quota.rule.tenant.blacklist}") String blackList) { | |
29 | + super(intervalDurationMs, ttlMs, whiteList, blackList, "Rule Tenant"); | |
30 | + } | |
31 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 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.common.transport.quota.tenant; | |
17 | + | |
18 | +import org.springframework.beans.factory.annotation.Value; | |
19 | +import org.springframework.stereotype.Component; | |
20 | +import org.thingsboard.server.common.transport.quota.AbstractQuotaService; | |
21 | + | |
22 | +@Component | |
23 | +public class TenantQuotaService extends AbstractQuotaService { | |
24 | + | |
25 | + public TenantQuotaService(TenantMsgsIntervalRegistry requestRegistry, TenantRequestLimitPolicy requestsPolicy, | |
26 | + TenantIntervalRegistryCleaner registryCleaner, TenantIntervalRegistryLogger registryLogger, | |
27 | + @Value("${quota.rule.tenant.enabled}") boolean enabled) { | |
28 | + super(requestRegistry, requestsPolicy, registryCleaner, registryLogger, enabled); | |
29 | + } | |
30 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2018 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.common.transport.quota.tenant; | |
17 | + | |
18 | +import org.springframework.beans.factory.annotation.Value; | |
19 | +import org.springframework.stereotype.Component; | |
20 | +import org.thingsboard.server.common.transport.quota.RequestLimitPolicy; | |
21 | + | |
22 | +@Component | |
23 | +public class TenantRequestLimitPolicy extends RequestLimitPolicy { | |
24 | + | |
25 | + public TenantRequestLimitPolicy(@Value("${quota.rule.tenant.limit}") long limit) { | |
26 | + super(limit); | |
27 | + } | |
28 | +} | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.common.transport.quota; |
17 | 17 | |
18 | 18 | import org.junit.Test; |
19 | +import org.thingsboard.server.common.transport.quota.host.HostRequestLimitPolicy; | |
19 | 20 | |
20 | 21 | import static org.junit.Assert.assertFalse; |
21 | 22 | import static org.junit.Assert.assertTrue; | ... | ... |
... | ... | @@ -17,9 +17,7 @@ package org.thingsboard.server.common.transport.quota; |
17 | 17 | |
18 | 18 | import org.junit.Before; |
19 | 19 | import org.junit.Test; |
20 | -import org.thingsboard.server.common.transport.quota.inmemory.HostRequestIntervalRegistry; | |
21 | -import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; | |
22 | -import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; | |
20 | +import org.thingsboard.server.common.transport.quota.host.*; | |
23 | 21 | |
24 | 22 | import static org.junit.Assert.assertFalse; |
25 | 23 | import static org.junit.Assert.assertTrue; |
... | ... | @@ -35,8 +33,8 @@ public class HostRequestsQuotaServiceTest { |
35 | 33 | |
36 | 34 | private HostRequestIntervalRegistry requestRegistry = mock(HostRequestIntervalRegistry.class); |
37 | 35 | private HostRequestLimitPolicy requestsPolicy = mock(HostRequestLimitPolicy.class); |
38 | - private IntervalRegistryCleaner registryCleaner = mock(IntervalRegistryCleaner.class); | |
39 | - private IntervalRegistryLogger registryLogger = mock(IntervalRegistryLogger.class); | |
36 | + private HostIntervalRegistryCleaner registryCleaner = mock(HostIntervalRegistryCleaner.class); | |
37 | + private HostIntervalRegistryLogger registryLogger = mock(HostIntervalRegistryLogger.class); | |
40 | 38 | |
41 | 39 | @Before |
42 | 40 | public void init() { | ... | ... |
... | ... | @@ -15,11 +15,9 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.transport.quota.inmemory; |
17 | 17 | |
18 | -import com.google.common.collect.Sets; | |
19 | 18 | import org.junit.Before; |
20 | 19 | import org.junit.Test; |
21 | - | |
22 | -import java.util.Collections; | |
20 | +import org.thingsboard.server.common.transport.quota.host.HostRequestIntervalRegistry; | |
23 | 21 | |
24 | 22 | import static org.junit.Assert.assertEquals; |
25 | 23 | ... | ... |
... | ... | @@ -18,6 +18,8 @@ package org.thingsboard.server.common.transport.quota.inmemory; |
18 | 18 | import com.google.common.collect.ImmutableMap; |
19 | 19 | import org.junit.Before; |
20 | 20 | import org.junit.Test; |
21 | +import org.thingsboard.server.common.transport.quota.host.HostIntervalRegistryLogger; | |
22 | +import org.thingsboard.server.common.transport.quota.host.HostRequestIntervalRegistry; | |
21 | 23 | |
22 | 24 | import java.util.Collections; |
23 | 25 | import java.util.Map; |
... | ... | @@ -37,7 +39,7 @@ public class IntervalRegistryLoggerTest { |
37 | 39 | |
38 | 40 | @Before |
39 | 41 | public void init() { |
40 | - logger = new IntervalRegistryLogger(3, 10, requestRegistry); | |
42 | + logger = new HostIntervalRegistryLogger(3, 10, requestRegistry); | |
41 | 43 | } |
42 | 44 | |
43 | 45 | @Test | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/queue/db/MsgAck.java
renamed from
dao/src/main/java/org/thingsboard/server/dao/queue/db/nosql/MsgAck.java
... | ... | @@ -13,7 +13,7 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.dao.queue.db.nosql; | |
16 | +package org.thingsboard.server.dao.queue.db; | |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | 19 | import lombok.EqualsAndHashCode; | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/queue/db/UnprocessedMsgFilter.java
renamed from
dao/src/main/java/org/thingsboard/server/dao/queue/db/nosql/UnprocessedMsgFilter.java
... | ... | @@ -13,10 +13,11 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.dao.queue.db.nosql; | |
16 | +package org.thingsboard.server.dao.queue.db; | |
17 | 17 | |
18 | 18 | import org.springframework.stereotype.Component; |
19 | 19 | import org.thingsboard.server.common.msg.TbMsg; |
20 | +import org.thingsboard.server.dao.queue.db.MsgAck; | |
20 | 21 | |
21 | 22 | import java.util.Collection; |
22 | 23 | import java.util.List; | ... | ... |
... | ... | @@ -26,6 +26,8 @@ import org.springframework.stereotype.Component; |
26 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
27 | 27 | import org.thingsboard.server.common.msg.TbMsg; |
28 | 28 | import org.thingsboard.server.dao.queue.MsgQueue; |
29 | +import org.thingsboard.server.dao.queue.db.MsgAck; | |
30 | +import org.thingsboard.server.dao.queue.db.UnprocessedMsgFilter; | |
29 | 31 | import org.thingsboard.server.dao.queue.db.repository.AckRepository; |
30 | 32 | import org.thingsboard.server.dao.queue.db.repository.MsgRepository; |
31 | 33 | import org.thingsboard.server.dao.util.NoSqlDao; | ... | ... |
... | ... | @@ -26,7 +26,7 @@ import com.google.common.util.concurrent.ListenableFuture; |
26 | 26 | import org.springframework.beans.factory.annotation.Value; |
27 | 27 | import org.springframework.stereotype.Component; |
28 | 28 | import org.thingsboard.server.dao.nosql.CassandraAbstractDao; |
29 | -import org.thingsboard.server.dao.queue.db.nosql.MsgAck; | |
29 | +import org.thingsboard.server.dao.queue.db.MsgAck; | |
30 | 30 | import org.thingsboard.server.dao.queue.db.repository.AckRepository; |
31 | 31 | import org.thingsboard.server.dao.util.NoSqlDao; |
32 | 32 | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | package org.thingsboard.server.dao.queue.db.repository; |
17 | 17 | |
18 | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | -import org.thingsboard.server.dao.queue.db.nosql.MsgAck; | |
19 | +import org.thingsboard.server.dao.queue.db.MsgAck; | |
20 | 20 | |
21 | 21 | import java.util.List; |
22 | 22 | import java.util.UUID; | ... | ... |
... | ... | @@ -16,6 +16,7 @@ |
16 | 16 | package org.thingsboard.server.dao.relation; |
17 | 17 | |
18 | 18 | import com.google.common.base.Function; |
19 | +import com.google.common.util.concurrent.AsyncFunction; | |
19 | 20 | import com.google.common.util.concurrent.Futures; |
20 | 21 | import com.google.common.util.concurrent.ListenableFuture; |
21 | 22 | import lombok.extern.slf4j.Slf4j; |
... | ... | @@ -176,97 +177,65 @@ public class BaseRelationService implements RelationService { |
176 | 177 | } |
177 | 178 | |
178 | 179 | @Override |
179 | - public boolean deleteEntityRelations(EntityId entity) { | |
180 | - Cache cache = cacheManager.getCache(RELATIONS_CACHE); | |
181 | - log.trace("Executing deleteEntityRelations [{}]", entity); | |
182 | - validate(entity); | |
183 | - List<ListenableFuture<List<EntityRelation>>> inboundRelationsList = new ArrayList<>(); | |
184 | - for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { | |
185 | - inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup)); | |
186 | - } | |
187 | - ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList); | |
188 | - ListenableFuture<List<Boolean>> inboundDeletions = Futures.transform(inboundRelations, relations -> | |
189 | - getBooleans(relations, cache, true)); | |
190 | - | |
191 | - ListenableFuture<Boolean> inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction()); | |
192 | - boolean inboundDeleteResult = false; | |
180 | + public void deleteEntityRelations(EntityId entityId) { | |
193 | 181 | try { |
194 | - inboundDeleteResult = inboundFuture.get(); | |
182 | + deleteEntityRelationsAsync(entityId).get(); | |
195 | 183 | } catch (InterruptedException | ExecutionException e) { |
196 | - log.error("Error deleting entity inbound relations", e); | |
197 | - } | |
198 | - | |
199 | - List<ListenableFuture<List<EntityRelation>>> outboundRelationsList = new ArrayList<>(); | |
200 | - for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { | |
201 | - outboundRelationsList.add(relationDao.findAllByFrom(entity, typeGroup)); | |
202 | - } | |
203 | - ListenableFuture<List<List<EntityRelation>>> outboundRelations = Futures.allAsList(outboundRelationsList); | |
204 | - Futures.transform(outboundRelations, relations -> getBooleans(relations, cache, false)); | |
205 | - | |
206 | - boolean outboundDeleteResult = relationDao.deleteOutboundRelations(entity); | |
207 | - return inboundDeleteResult && outboundDeleteResult; | |
208 | - } | |
209 | - | |
210 | - private List<Boolean> getBooleans(List<List<EntityRelation>> relations, Cache cache, boolean isRemove) { | |
211 | - List<Boolean> results = new ArrayList<>(); | |
212 | - for (List<EntityRelation> relationList : relations) { | |
213 | - relationList.forEach(relation -> checkFromDeleteSync(cache, results, relation, isRemove)); | |
184 | + throw new RuntimeException(e); | |
214 | 185 | } |
215 | - return results; | |
216 | - } | |
217 | - | |
218 | - private void checkFromDeleteSync(Cache cache, List<Boolean> results, EntityRelation relation, boolean isRemove) { | |
219 | - if (isRemove) { | |
220 | - results.add(relationDao.deleteRelation(relation)); | |
221 | - } | |
222 | - cacheEviction(relation, cache); | |
223 | 186 | } |
224 | 187 | |
225 | 188 | @Override |
226 | - public ListenableFuture<Boolean> deleteEntityRelationsAsync(EntityId entity) { | |
189 | + public ListenableFuture<Void> deleteEntityRelationsAsync(EntityId entityId) { | |
227 | 190 | Cache cache = cacheManager.getCache(RELATIONS_CACHE); |
228 | - log.trace("Executing deleteEntityRelationsAsync [{}]", entity); | |
229 | - validate(entity); | |
191 | + log.trace("Executing deleteEntityRelationsAsync [{}]", entityId); | |
192 | + validate(entityId); | |
230 | 193 | List<ListenableFuture<List<EntityRelation>>> inboundRelationsList = new ArrayList<>(); |
231 | 194 | for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { |
232 | - inboundRelationsList.add(relationDao.findAllByTo(entity, typeGroup)); | |
195 | + inboundRelationsList.add(relationDao.findAllByTo(entityId, typeGroup)); | |
233 | 196 | } |
197 | + | |
234 | 198 | ListenableFuture<List<List<EntityRelation>>> inboundRelations = Futures.allAsList(inboundRelationsList); |
199 | + | |
200 | + List<ListenableFuture<List<EntityRelation>>> outboundRelationsList = new ArrayList<>(); | |
201 | + for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { | |
202 | + outboundRelationsList.add(relationDao.findAllByFrom(entityId, typeGroup)); | |
203 | + } | |
204 | + | |
205 | + ListenableFuture<List<List<EntityRelation>>> outboundRelations = Futures.allAsList(outboundRelationsList); | |
206 | + | |
235 | 207 | ListenableFuture<List<Boolean>> inboundDeletions = Futures.transformAsync(inboundRelations, |
236 | 208 | relations -> { |
237 | - List<ListenableFuture<Boolean>> results = getListenableFutures(relations, cache, true); | |
209 | + List<ListenableFuture<Boolean>> results = deleteRelationGroupsAsync(relations, cache, true); | |
238 | 210 | return Futures.allAsList(results); |
239 | 211 | }); |
240 | 212 | |
241 | - ListenableFuture<Boolean> inboundFuture = Futures.transform(inboundDeletions, getListToBooleanFunction()); | |
213 | + ListenableFuture<List<Boolean>> outboundDeletions = Futures.transformAsync(outboundRelations, | |
214 | + relations -> { | |
215 | + List<ListenableFuture<Boolean>> results = deleteRelationGroupsAsync(relations, cache, false); | |
216 | + return Futures.allAsList(results); | |
217 | + }); | |
242 | 218 | |
243 | - List<ListenableFuture<List<EntityRelation>>> outboundRelationsList = new ArrayList<>(); | |
244 | - for (RelationTypeGroup typeGroup : RelationTypeGroup.values()) { | |
245 | - outboundRelationsList.add(relationDao.findAllByFrom(entity, typeGroup)); | |
246 | - } | |
247 | - ListenableFuture<List<List<EntityRelation>>> outboundRelations = Futures.allAsList(outboundRelationsList); | |
248 | - Futures.transformAsync(outboundRelations, relations -> { | |
249 | - List<ListenableFuture<Boolean>> results = getListenableFutures(relations, cache, false); | |
250 | - return Futures.allAsList(results); | |
251 | - }); | |
219 | + ListenableFuture<List<List<Boolean>>> deletionsFuture = Futures.allAsList(inboundDeletions, outboundDeletions); | |
252 | 220 | |
253 | - ListenableFuture<Boolean> outboundFuture = relationDao.deleteOutboundRelationsAsync(entity); | |
254 | - return Futures.transform(Futures.allAsList(Arrays.asList(inboundFuture, outboundFuture)), getListToBooleanFunction()); | |
221 | + return Futures.transform(Futures.transformAsync(deletionsFuture, (deletions) -> relationDao.deleteOutboundRelationsAsync(entityId)), result -> null); | |
255 | 222 | } |
256 | 223 | |
257 | - private List<ListenableFuture<Boolean>> getListenableFutures(List<List<EntityRelation>> relations, Cache cache, boolean isRemove) { | |
224 | + private List<ListenableFuture<Boolean>> deleteRelationGroupsAsync(List<List<EntityRelation>> relations, Cache cache, boolean deleteFromDb) { | |
258 | 225 | List<ListenableFuture<Boolean>> results = new ArrayList<>(); |
259 | 226 | for (List<EntityRelation> relationList : relations) { |
260 | - relationList.forEach(relation -> checkFromDeleteAsync(cache, results, relation, isRemove)); | |
227 | + relationList.forEach(relation -> results.add(deleteAsync(cache, relation, deleteFromDb))); | |
261 | 228 | } |
262 | 229 | return results; |
263 | 230 | } |
264 | 231 | |
265 | - private void checkFromDeleteAsync(Cache cache, List<ListenableFuture<Boolean>> results, EntityRelation relation, boolean isRemove) { | |
266 | - if (isRemove) { | |
267 | - results.add(relationDao.deleteRelationAsync(relation)); | |
268 | - } | |
232 | + private ListenableFuture<Boolean> deleteAsync(Cache cache, EntityRelation relation, boolean deleteFromDb) { | |
269 | 233 | cacheEviction(relation, cache); |
234 | + if (deleteFromDb) { | |
235 | + return relationDao.deleteRelationAsync(relation); | |
236 | + } else { | |
237 | + return Futures.immediateFuture(false); | |
238 | + } | |
270 | 239 | } |
271 | 240 | |
272 | 241 | private void cacheEviction(EntityRelation relation, Cache cache) { | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.relation.EntityRelationsQuery; |
23 | 23 | import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
24 | 24 | |
25 | 25 | import java.util.List; |
26 | +import java.util.concurrent.ExecutionException; | |
26 | 27 | |
27 | 28 | /** |
28 | 29 | * Created by ashvayka on 27.04.17. |
... | ... | @@ -47,9 +48,9 @@ public interface RelationService { |
47 | 48 | |
48 | 49 | ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup); |
49 | 50 | |
50 | - boolean deleteEntityRelations(EntityId entity); | |
51 | + void deleteEntityRelations(EntityId entity); | |
51 | 52 | |
52 | - ListenableFuture<Boolean> deleteEntityRelationsAsync(EntityId entity); | |
53 | + ListenableFuture<Void> deleteEntityRelationsAsync(EntityId entity); | |
53 | 54 | |
54 | 55 | List<EntityRelation> findByFrom(EntityId from, RelationTypeGroup typeGroup); |
55 | 56 | ... | ... |
... | ... | @@ -127,39 +127,35 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple |
127 | 127 | @Override |
128 | 128 | public boolean deleteRelation(EntityRelation relation) { |
129 | 129 | RelationCompositeKey key = new RelationCompositeKey(relation); |
130 | - boolean relationExistsBeforeDelete = relationRepository.exists(key); | |
131 | - relationRepository.delete(key); | |
132 | - return relationExistsBeforeDelete; | |
130 | + return deleteRelationIfExists(key); | |
133 | 131 | } |
134 | 132 | |
135 | 133 | @Override |
136 | 134 | public ListenableFuture<Boolean> deleteRelationAsync(EntityRelation relation) { |
137 | 135 | RelationCompositeKey key = new RelationCompositeKey(relation); |
138 | 136 | return service.submit( |
139 | - () -> { | |
140 | - boolean relationExistsBeforeDelete = relationRepository.exists(key); | |
141 | - relationRepository.delete(key); | |
142 | - return relationExistsBeforeDelete; | |
143 | - }); | |
137 | + () -> deleteRelationIfExists(key)); | |
144 | 138 | } |
145 | 139 | |
146 | 140 | @Override |
147 | 141 | public boolean deleteRelation(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
148 | 142 | RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup); |
149 | - boolean relationExistsBeforeDelete = relationRepository.exists(key); | |
150 | - relationRepository.delete(key); | |
151 | - return relationExistsBeforeDelete; | |
143 | + return deleteRelationIfExists(key); | |
152 | 144 | } |
153 | 145 | |
154 | 146 | @Override |
155 | 147 | public ListenableFuture<Boolean> deleteRelationAsync(EntityId from, EntityId to, String relationType, RelationTypeGroup typeGroup) { |
156 | 148 | RelationCompositeKey key = getRelationCompositeKey(from, to, relationType, typeGroup); |
157 | 149 | return service.submit( |
158 | - () -> { | |
159 | - boolean relationExistsBeforeDelete = relationRepository.exists(key); | |
160 | - relationRepository.delete(key); | |
161 | - return relationExistsBeforeDelete; | |
162 | - }); | |
150 | + () -> deleteRelationIfExists(key)); | |
151 | + } | |
152 | + | |
153 | + private boolean deleteRelationIfExists(RelationCompositeKey key) { | |
154 | + boolean relationExistsBeforeDelete = relationRepository.exists(key); | |
155 | + if (relationExistsBeforeDelete) { | |
156 | + relationRepository.delete(key); | |
157 | + } | |
158 | + return relationExistsBeforeDelete; | |
163 | 159 | } |
164 | 160 | |
165 | 161 | @Override |
... | ... | @@ -167,7 +163,9 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple |
167 | 163 | boolean relationExistsBeforeDelete = relationRepository |
168 | 164 | .findAllByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()) |
169 | 165 | .size() > 0; |
170 | - relationRepository.deleteByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()); | |
166 | + if (relationExistsBeforeDelete) { | |
167 | + relationRepository.deleteByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()); | |
168 | + } | |
171 | 169 | return relationExistsBeforeDelete; |
172 | 170 | } |
173 | 171 | |
... | ... | @@ -178,7 +176,9 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple |
178 | 176 | boolean relationExistsBeforeDelete = relationRepository |
179 | 177 | .findAllByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()) |
180 | 178 | .size() > 0; |
181 | - relationRepository.deleteByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()); | |
179 | + if (relationExistsBeforeDelete) { | |
180 | + relationRepository.deleteByFromIdAndFromType(UUIDConverter.fromTimeUUID(entity.getId()), entity.getEntityType().name()); | |
181 | + } | |
182 | 182 | return relationExistsBeforeDelete; |
183 | 183 | }); |
184 | 184 | } | ... | ... |
... | ... | @@ -18,6 +18,8 @@ package org.thingsboard.server.dao.queue.db.nosql; |
18 | 18 | import com.google.common.collect.Lists; |
19 | 19 | import org.junit.Test; |
20 | 20 | import org.thingsboard.server.common.msg.TbMsg; |
21 | +import org.thingsboard.server.dao.queue.db.MsgAck; | |
22 | +import org.thingsboard.server.dao.queue.db.UnprocessedMsgFilter; | |
21 | 23 | |
22 | 24 | import java.util.Collection; |
23 | 25 | import java.util.List; | ... | ... |
... | ... | @@ -21,9 +21,9 @@ import com.google.common.util.concurrent.ListenableFuture; |
21 | 21 | import org.junit.Test; |
22 | 22 | import org.springframework.beans.factory.annotation.Autowired; |
23 | 23 | import org.springframework.test.util.ReflectionTestUtils; |
24 | -import org.thingsboard.server.dao.queue.db.nosql.MsgAck; | |
25 | 24 | import org.thingsboard.server.dao.service.AbstractServiceTest; |
26 | 25 | import org.thingsboard.server.dao.service.DaoNoSqlTest; |
26 | +import org.thingsboard.server.dao.queue.db.MsgAck; | |
27 | 27 | |
28 | 28 | import java.util.List; |
29 | 29 | import java.util.UUID; | ... | ... |
... | ... | @@ -96,7 +96,7 @@ public abstract class BaseRelationServiceTest extends AbstractServiceTest { |
96 | 96 | saveRelation(relationA); |
97 | 97 | saveRelation(relationB); |
98 | 98 | |
99 | - Assert.assertTrue(relationService.deleteEntityRelationsAsync(childId).get()); | |
99 | + Assert.assertNull(relationService.deleteEntityRelationsAsync(childId).get()); | |
100 | 100 | |
101 | 101 | Assert.assertFalse(relationService.checkRelation(parentId, childId, EntityRelation.CONTAINS_TYPE, RelationTypeGroup.COMMON).get()); |
102 | 102 | ... | ... |
... | ... | @@ -27,6 +27,7 @@ import org.springframework.stereotype.Service; |
27 | 27 | import org.thingsboard.server.common.transport.SessionMsgProcessor; |
28 | 28 | import org.thingsboard.server.common.transport.auth.DeviceAuthService; |
29 | 29 | import org.thingsboard.server.common.transport.quota.QuotaService; |
30 | +import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; | |
30 | 31 | import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor; |
31 | 32 | |
32 | 33 | import javax.annotation.PostConstruct; |
... | ... | @@ -55,7 +56,7 @@ public class CoapTransportService { |
55 | 56 | private DeviceAuthService authService; |
56 | 57 | |
57 | 58 | @Autowired(required = false) |
58 | - private QuotaService quotaService; | |
59 | + private HostRequestsQuotaService quotaService; | |
59 | 60 | |
60 | 61 | |
61 | 62 | @Value("${coap.bind_address}") | ... | ... |
... | ... | @@ -50,7 +50,7 @@ import org.thingsboard.server.common.msg.session.*; |
50 | 50 | import org.thingsboard.server.common.transport.SessionMsgProcessor; |
51 | 51 | import org.thingsboard.server.common.transport.auth.DeviceAuthResult; |
52 | 52 | import org.thingsboard.server.common.transport.auth.DeviceAuthService; |
53 | -import org.thingsboard.server.common.transport.quota.QuotaService; | |
53 | +import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; | |
54 | 54 | |
55 | 55 | import java.util.ArrayList; |
56 | 56 | import java.util.List; |
... | ... | @@ -134,8 +134,8 @@ public class CoapServerTest { |
134 | 134 | } |
135 | 135 | |
136 | 136 | @Bean |
137 | - public static QuotaService quotaService() { | |
138 | - return key -> false; | |
137 | + public static HostRequestsQuotaService quotaService() { | |
138 | + return new HostRequestsQuotaService(null, null, null, null, false); | |
139 | 139 | } |
140 | 140 | } |
141 | 141 | ... | ... |
... | ... | @@ -36,6 +36,7 @@ import org.thingsboard.server.common.transport.SessionMsgProcessor; |
36 | 36 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
37 | 37 | import org.thingsboard.server.common.transport.auth.DeviceAuthService; |
38 | 38 | import org.thingsboard.server.common.transport.quota.QuotaService; |
39 | +import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; | |
39 | 40 | import org.thingsboard.server.transport.http.session.HttpSessionCtx; |
40 | 41 | |
41 | 42 | import javax.servlet.http.HttpServletRequest; |
... | ... | @@ -61,7 +62,7 @@ public class DeviceApiController { |
61 | 62 | private DeviceAuthService authService; |
62 | 63 | |
63 | 64 | @Autowired(required = false) |
64 | - private QuotaService quotaService; | |
65 | + private HostRequestsQuotaService quotaService; | |
65 | 66 | |
66 | 67 | @RequestMapping(value = "/{deviceToken}/attributes", method = RequestMethod.GET, produces = "application/json") |
67 | 68 | public DeferredResult<ResponseEntity> getDeviceAttributes(@PathVariable("deviceToken") String deviceToken, | ... | ... |
... | ... | @@ -29,7 +29,7 @@ import org.springframework.context.ApplicationContext; |
29 | 29 | import org.springframework.stereotype.Service; |
30 | 30 | import org.thingsboard.server.common.transport.SessionMsgProcessor; |
31 | 31 | import org.thingsboard.server.common.transport.auth.DeviceAuthService; |
32 | -import org.thingsboard.server.common.transport.quota.QuotaService; | |
32 | +import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; | |
33 | 33 | import org.thingsboard.server.dao.device.DeviceService; |
34 | 34 | import org.thingsboard.server.dao.relation.RelationService; |
35 | 35 | import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; |
... | ... | @@ -67,7 +67,7 @@ public class MqttTransportService { |
67 | 67 | private MqttSslHandlerProvider sslHandlerProvider; |
68 | 68 | |
69 | 69 | @Autowired(required = false) |
70 | - private QuotaService quotaService; | |
70 | + private HostRequestsQuotaService quotaService; | |
71 | 71 | |
72 | 72 | @Value("${mqtt.bind_address}") |
73 | 73 | private String host; | ... | ... |