43 changed files with 1221 additions and 2566 deletions
@ -1,67 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota; |
|||
|
|||
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.KeyBasedIntervalRegistry; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.annotation.PreDestroy; |
|||
|
|||
public class AbstractQuotaService implements QuotaService { |
|||
|
|||
private final KeyBasedIntervalRegistry requestRegistry; |
|||
private final RequestLimitPolicy requestsPolicy; |
|||
private final IntervalRegistryCleaner registryCleaner; |
|||
private final IntervalRegistryLogger registryLogger; |
|||
private final boolean enabled; |
|||
|
|||
public AbstractQuotaService(KeyBasedIntervalRegistry requestRegistry, RequestLimitPolicy requestsPolicy, |
|||
IntervalRegistryCleaner registryCleaner, IntervalRegistryLogger registryLogger, |
|||
boolean enabled) { |
|||
this.requestRegistry = requestRegistry; |
|||
this.requestsPolicy = requestsPolicy; |
|||
this.registryCleaner = registryCleaner; |
|||
this.registryLogger = registryLogger; |
|||
this.enabled = enabled; |
|||
} |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
if (enabled) { |
|||
registryCleaner.schedule(); |
|||
registryLogger.schedule(); |
|||
} |
|||
} |
|||
|
|||
@PreDestroy |
|||
public void close() { |
|||
if (enabled) { |
|||
registryCleaner.stop(); |
|||
registryLogger.stop(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public boolean isQuotaExceeded(String key) { |
|||
if (enabled) { |
|||
long count = requestRegistry.tick(key); |
|||
return !requestsPolicy.isValid(count); |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
@ -1,45 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public final class Clock { |
|||
|
|||
private static long time = 0L; |
|||
|
|||
private Clock() { |
|||
} |
|||
|
|||
|
|||
public static long millis() { |
|||
return time == 0 ? System.currentTimeMillis() : time; |
|||
} |
|||
|
|||
public static void setMillis(long millis) { |
|||
time = millis; |
|||
} |
|||
|
|||
public static void shift(long delta) { |
|||
time += delta; |
|||
} |
|||
|
|||
public static void reset() { |
|||
time = 0; |
|||
} |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public interface QuotaService { |
|||
|
|||
boolean isQuotaExceeded(String key); |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota; |
|||
|
|||
|
|||
public abstract class RequestLimitPolicy { |
|||
|
|||
private final long limit; |
|||
|
|||
public RequestLimitPolicy(long limit) { |
|||
this.limit = limit; |
|||
} |
|||
|
|||
public boolean isValid(long currentValue) { |
|||
return currentValue <= limit; |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.host; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; |
|||
|
|||
@Component |
|||
public class HostIntervalRegistryCleaner extends IntervalRegistryCleaner { |
|||
|
|||
public HostIntervalRegistryCleaner(HostRequestIntervalRegistry intervalRegistry, |
|||
@Value("${quota.host.cleanPeriodMs}") long cleanPeriodMs) { |
|||
super(intervalRegistry, cleanPeriodMs); |
|||
} |
|||
} |
|||
@ -1,52 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.host; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; |
|||
|
|||
import java.util.Map; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Component |
|||
@Slf4j |
|||
public class HostIntervalRegistryLogger extends IntervalRegistryLogger { |
|||
|
|||
private final long logIntervalMin; |
|||
|
|||
public HostIntervalRegistryLogger(@Value("${quota.host.log.topSize}") int topSize, |
|||
@Value("${quota.host.log.intervalMin}") long logIntervalMin, |
|||
HostRequestIntervalRegistry intervalRegistry) { |
|||
super(topSize, logIntervalMin, intervalRegistry); |
|||
this.logIntervalMin = logIntervalMin; |
|||
} |
|||
|
|||
protected void log(Map<String, Long> top, int uniqHosts, long requestsCount) { |
|||
long rps = requestsCount / TimeUnit.MINUTES.toSeconds(logIntervalMin); |
|||
StringBuilder builder = new StringBuilder("Quota Statistic : "); |
|||
builder.append("uniqHosts : ").append(uniqHosts).append("; "); |
|||
builder.append("requestsCount : ").append(requestsCount).append("; "); |
|||
builder.append("RPS : ").append(rps).append(" "); |
|||
builder.append("top -> "); |
|||
for (Map.Entry<String, Long> host : top.entrySet()) { |
|||
builder.append(host.getKey()).append(" : ").append(host.getValue()).append("; "); |
|||
} |
|||
|
|||
log.info(builder.toString()); |
|||
} |
|||
} |
|||
@ -1,37 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.host; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.KeyBasedIntervalRegistry; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
@Component |
|||
@Slf4j |
|||
public class HostRequestIntervalRegistry extends KeyBasedIntervalRegistry { |
|||
|
|||
public HostRequestIntervalRegistry(@Value("${quota.host.intervalMs}") long intervalDurationMs, |
|||
@Value("${quota.host.ttlMs}") long ttlMs, |
|||
@Value("${quota.host.whitelist}") String whiteList, |
|||
@Value("${quota.host.blacklist}") String blackList) { |
|||
super(intervalDurationMs, ttlMs, whiteList, blackList, "host"); |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.host; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.quota.RequestLimitPolicy; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
@Component |
|||
public class HostRequestLimitPolicy extends RequestLimitPolicy { |
|||
|
|||
public HostRequestLimitPolicy(@Value("${quota.host.limit}") long limit) { |
|||
super(limit); |
|||
} |
|||
|
|||
} |
|||
@ -1,37 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.host; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.transport.quota.AbstractQuotaService; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
@Service |
|||
@Slf4j |
|||
public class HostRequestsQuotaService extends AbstractQuotaService { |
|||
|
|||
public HostRequestsQuotaService(HostRequestIntervalRegistry requestRegistry, HostRequestLimitPolicy requestsPolicy, |
|||
HostIntervalRegistryCleaner registryCleaner, HostIntervalRegistryLogger registryLogger, |
|||
@Value("${quota.host.enabled}") boolean enabled) { |
|||
super(requestRegistry, requestsPolicy, registryCleaner, registryLogger, enabled); |
|||
} |
|||
|
|||
} |
|||
@ -1,68 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.inmemory; |
|||
|
|||
|
|||
import org.thingsboard.server.common.transport.quota.Clock; |
|||
|
|||
import java.util.concurrent.atomic.LongAdder; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class IntervalCount { |
|||
|
|||
private final LongAdder adder = new LongAdder(); |
|||
private final long intervalDurationMs; |
|||
private volatile long startTime; |
|||
private volatile long lastTickTime; |
|||
|
|||
public IntervalCount(long intervalDurationMs) { |
|||
this.intervalDurationMs = intervalDurationMs; |
|||
startTime = Clock.millis(); |
|||
} |
|||
|
|||
public long resetIfExpiredAndTick() { |
|||
if (isExpired()) { |
|||
reset(); |
|||
} |
|||
tick(); |
|||
return adder.sum(); |
|||
} |
|||
|
|||
public long silenceDuration() { |
|||
return Clock.millis() - lastTickTime; |
|||
} |
|||
|
|||
public long getCount() { |
|||
return adder.sum(); |
|||
} |
|||
|
|||
private void tick() { |
|||
adder.add(1); |
|||
lastTickTime = Clock.millis(); |
|||
} |
|||
|
|||
private void reset() { |
|||
adder.reset(); |
|||
startTime = Clock.millis(); |
|||
} |
|||
|
|||
private boolean isExpired() { |
|||
return (Clock.millis() - startTime) > intervalDurationMs; |
|||
} |
|||
} |
|||
@ -1,62 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.inmemory; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.concurrent.Executors; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
@Slf4j |
|||
public abstract class IntervalRegistryCleaner { |
|||
|
|||
private final KeyBasedIntervalRegistry intervalRegistry; |
|||
private final long cleanPeriodMs; |
|||
private ScheduledExecutorService executor; |
|||
|
|||
public IntervalRegistryCleaner(KeyBasedIntervalRegistry intervalRegistry, long cleanPeriodMs) { |
|||
this.intervalRegistry = intervalRegistry; |
|||
this.cleanPeriodMs = cleanPeriodMs; |
|||
} |
|||
|
|||
public void schedule() { |
|||
if (executor != null) { |
|||
throw new IllegalStateException("Registry Cleaner already scheduled"); |
|||
} |
|||
executor = Executors.newSingleThreadScheduledExecutor(); |
|||
executor.scheduleAtFixedRate(this::clean, cleanPeriodMs, cleanPeriodMs, TimeUnit.MILLISECONDS); |
|||
} |
|||
|
|||
public void stop() { |
|||
if (executor != null) { |
|||
executor.shutdown(); |
|||
} |
|||
} |
|||
|
|||
public void clean() { |
|||
try { |
|||
intervalRegistry.clean(); |
|||
} catch (RuntimeException ex) { |
|||
log.error("Could not clear Interval Registry", ex); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -1,79 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.inmemory; |
|||
|
|||
import com.google.common.collect.MinMaxPriorityQueue; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.Comparator; |
|||
import java.util.Map; |
|||
import java.util.concurrent.Executors; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.function.Function; |
|||
import java.util.stream.Collectors; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
@Slf4j |
|||
public abstract class IntervalRegistryLogger { |
|||
|
|||
private final int topSize; |
|||
private final KeyBasedIntervalRegistry intervalRegistry; |
|||
private final long logIntervalMin; |
|||
private ScheduledExecutorService executor; |
|||
|
|||
public IntervalRegistryLogger(int topSize, long logIntervalMin, KeyBasedIntervalRegistry intervalRegistry) { |
|||
this.topSize = topSize; |
|||
this.logIntervalMin = logIntervalMin; |
|||
this.intervalRegistry = intervalRegistry; |
|||
} |
|||
|
|||
public void schedule() { |
|||
if (executor != null) { |
|||
throw new IllegalStateException("Registry Cleaner already scheduled"); |
|||
} |
|||
executor = Executors.newSingleThreadScheduledExecutor(); |
|||
executor.scheduleAtFixedRate(this::logStatistic, logIntervalMin, logIntervalMin, TimeUnit.MINUTES); |
|||
} |
|||
|
|||
public void stop() { |
|||
if (executor != null) { |
|||
executor.shutdown(); |
|||
} |
|||
} |
|||
|
|||
public void logStatistic() { |
|||
Map<String, Long> registryContent = intervalRegistry.getContent(); |
|||
int uniqHosts = registryContent.size(); |
|||
long requestsCount = registryContent.values().stream().mapToLong(i -> i).sum(); |
|||
Map<String, Long> top = getTopElements(registryContent); |
|||
log(top, uniqHosts, requestsCount); |
|||
} |
|||
|
|||
protected Map<String, Long> getTopElements(Map<String, Long> countMap) { |
|||
MinMaxPriorityQueue<Map.Entry<String, Long>> topQueue = MinMaxPriorityQueue |
|||
.orderedBy(Comparator.comparing((Function<Map.Entry<String, Long>, Long>) Map.Entry::getValue).reversed()) |
|||
.maximumSize(topSize) |
|||
.create(countMap.entrySet()); |
|||
|
|||
return topQueue.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
|||
} |
|||
|
|||
protected abstract void log(Map<String, Long> top, int uniqHosts, long requestsCount); |
|||
} |
|||
@ -1,73 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.inmemory; |
|||
|
|||
import com.google.common.collect.Sets; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
|
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Slf4j |
|||
public abstract class KeyBasedIntervalRegistry { |
|||
|
|||
private final Map<String, IntervalCount> hostCounts = new ConcurrentHashMap<>(); |
|||
private final long intervalDurationMs; |
|||
private final long ttlMs; |
|||
private final Set<String> whiteList; |
|||
private final Set<String> blackList; |
|||
|
|||
public KeyBasedIntervalRegistry(long intervalDurationMs, long ttlMs, String whiteList, String blackList, String name) { |
|||
this.intervalDurationMs = intervalDurationMs; |
|||
this.ttlMs = ttlMs; |
|||
this.whiteList = Sets.newHashSet(StringUtils.split(whiteList, ',')); |
|||
this.blackList = Sets.newHashSet(StringUtils.split(blackList, ',')); |
|||
validate(name); |
|||
} |
|||
|
|||
private void validate(String name) { |
|||
if (ttlMs < intervalDurationMs) { |
|||
log.warn("TTL for {} IntervalRegistry [{}] smaller than interval duration [{}]", name, ttlMs, intervalDurationMs); |
|||
} |
|||
log.info("Start {} KeyBasedIntervalRegistry with whitelist {}", name, whiteList); |
|||
log.info("Start {} KeyBasedIntervalRegistry with blacklist {}", name, blackList); |
|||
} |
|||
|
|||
public long tick(String clientHostId) { |
|||
IntervalCount intervalCount = hostCounts.computeIfAbsent(clientHostId, s -> new IntervalCount(intervalDurationMs)); |
|||
long currentCount = intervalCount.resetIfExpiredAndTick(); |
|||
if (whiteList.contains(clientHostId)) { |
|||
return 0; |
|||
} else if (blackList.contains(clientHostId)) { |
|||
return Long.MAX_VALUE; |
|||
} |
|||
return currentCount; |
|||
} |
|||
|
|||
public void clean() { |
|||
hostCounts.entrySet().removeIf(entry -> entry.getValue().silenceDuration() > ttlMs); |
|||
} |
|||
|
|||
public Map<String, Long> getContent() { |
|||
return hostCounts.entrySet().stream() |
|||
.collect(Collectors.toMap( |
|||
Map.Entry::getKey, |
|||
interval -> interval.getValue().getCount())); |
|||
} |
|||
} |
|||
@ -1,31 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.tenant; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryCleaner; |
|||
|
|||
@Component |
|||
@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) |
|||
public class TenantIntervalRegistryCleaner extends IntervalRegistryCleaner { |
|||
|
|||
public TenantIntervalRegistryCleaner(TenantMsgsIntervalRegistry intervalRegistry, |
|||
@Value("${quota.rule.tenant.cleanPeriodMs}") long cleanPeriodMs) { |
|||
super(intervalRegistry, cleanPeriodMs); |
|||
} |
|||
} |
|||
@ -1,54 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.tenant; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.IntervalRegistryLogger; |
|||
|
|||
import java.util.Map; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Slf4j |
|||
@Component |
|||
@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) |
|||
public class TenantIntervalRegistryLogger extends IntervalRegistryLogger { |
|||
|
|||
private final long logIntervalMin; |
|||
|
|||
public TenantIntervalRegistryLogger(@Value("${quota.rule.tenant.log.topSize}") int topSize, |
|||
@Value("${quota.rule.tenant.log.intervalMin}") long logIntervalMin, |
|||
TenantMsgsIntervalRegistry intervalRegistry) { |
|||
super(topSize, logIntervalMin, intervalRegistry); |
|||
this.logIntervalMin = logIntervalMin; |
|||
} |
|||
|
|||
protected void log(Map<String, Long> top, int uniqHosts, long requestsCount) { |
|||
long rps = requestsCount / TimeUnit.MINUTES.toSeconds(logIntervalMin); |
|||
StringBuilder builder = new StringBuilder("Tenant Quota Statistic : "); |
|||
builder.append("uniqTenants : ").append(uniqHosts).append("; "); |
|||
builder.append("requestsCount : ").append(requestsCount).append("; "); |
|||
builder.append("RPS : ").append(rps).append(" "); |
|||
builder.append("top -> "); |
|||
for (Map.Entry<String, Long> host : top.entrySet()) { |
|||
builder.append(host.getKey()).append(" : ").append(host.getValue()).append("; "); |
|||
} |
|||
|
|||
log.info(builder.toString()); |
|||
} |
|||
} |
|||
@ -1,33 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.tenant; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.quota.inmemory.KeyBasedIntervalRegistry; |
|||
|
|||
@Component |
|||
@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) |
|||
public class TenantMsgsIntervalRegistry extends KeyBasedIntervalRegistry { |
|||
|
|||
public TenantMsgsIntervalRegistry(@Value("${quota.rule.tenant.intervalMs}") long intervalDurationMs, |
|||
@Value("${quota.rule.tenant.ttlMs}") long ttlMs, |
|||
@Value("${quota.rule.tenant.whitelist}") String whiteList, |
|||
@Value("${quota.rule.tenant.blacklist}") String blackList) { |
|||
super(intervalDurationMs, ttlMs, whiteList, blackList, "Rule Tenant"); |
|||
} |
|||
} |
|||
@ -1,32 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.tenant; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.quota.AbstractQuotaService; |
|||
|
|||
@Component |
|||
@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) |
|||
public class TenantQuotaService extends AbstractQuotaService { |
|||
|
|||
public TenantQuotaService(TenantMsgsIntervalRegistry requestRegistry, TenantRequestLimitPolicy requestsPolicy, |
|||
TenantIntervalRegistryCleaner registryCleaner, TenantIntervalRegistryLogger registryLogger, |
|||
@Value("${quota.rule.tenant.enabled}") boolean enabled) { |
|||
super(requestRegistry, requestsPolicy, registryCleaner, registryLogger, enabled); |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.tenant; |
|||
|
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.transport.quota.RequestLimitPolicy; |
|||
|
|||
@Component |
|||
@ConditionalOnProperty(prefix = "quota.rule.tenant", value = "enabled", havingValue = "true", matchIfMissing = false) |
|||
public class TenantRequestLimitPolicy extends RequestLimitPolicy { |
|||
|
|||
public TenantRequestLimitPolicy(@Value("${quota.rule.tenant.limit}") long limit) { |
|||
super(limit); |
|||
} |
|||
} |
|||
@ -1,66 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota; |
|||
|
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
|
|||
import static org.junit.Assert.*; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class ClockTest { |
|||
|
|||
@Before |
|||
public void init() { |
|||
Clock.reset(); |
|||
} |
|||
|
|||
@After |
|||
public void clear() { |
|||
Clock.reset(); |
|||
} |
|||
|
|||
@Test |
|||
public void defaultClockUseSystemTime() { |
|||
assertFalse(Clock.millis() > System.currentTimeMillis()); |
|||
} |
|||
|
|||
@Test |
|||
public void timeCanBeSet() { |
|||
Clock.setMillis(100L); |
|||
assertEquals(100L, Clock.millis()); |
|||
} |
|||
|
|||
@Test |
|||
public void clockCanBeReseted() { |
|||
Clock.setMillis(100L); |
|||
assertEquals(100L, Clock.millis()); |
|||
Clock.reset(); |
|||
assertFalse(Clock.millis() > System.currentTimeMillis()); |
|||
} |
|||
|
|||
@Test |
|||
public void timeIsShifted() { |
|||
Clock.setMillis(100L); |
|||
Clock.shift(50L); |
|||
assertEquals(150L, Clock.millis()); |
|||
} |
|||
|
|||
} |
|||
@ -1,47 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota; |
|||
|
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.transport.quota.host.HostRequestLimitPolicy; |
|||
|
|||
import static org.junit.Assert.assertFalse; |
|||
import static org.junit.Assert.assertTrue; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class HostRequestLimitPolicyTest { |
|||
|
|||
private HostRequestLimitPolicy limitPolicy = new HostRequestLimitPolicy(10L); |
|||
|
|||
@Test |
|||
public void ifCurrentValueLessThenLimitItIsValid() { |
|||
assertTrue(limitPolicy.isValid(9)); |
|||
} |
|||
|
|||
@Test |
|||
public void ifCurrentValueEqualsToLimitItIsValid() { |
|||
assertTrue(limitPolicy.isValid(10)); |
|||
} |
|||
|
|||
@Test |
|||
public void ifCurrentValueGreaterThenLimitItIsValid() { |
|||
assertFalse(limitPolicy.isValid(11)); |
|||
} |
|||
|
|||
} |
|||
@ -1,78 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota; |
|||
|
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.transport.quota.host.HostIntervalRegistryCleaner; |
|||
import org.thingsboard.server.common.transport.quota.host.HostIntervalRegistryLogger; |
|||
import org.thingsboard.server.common.transport.quota.host.HostRequestIntervalRegistry; |
|||
import org.thingsboard.server.common.transport.quota.host.HostRequestLimitPolicy; |
|||
import org.thingsboard.server.common.transport.quota.host.HostRequestsQuotaService; |
|||
|
|||
import static org.junit.Assert.assertFalse; |
|||
import static org.junit.Assert.assertTrue; |
|||
import static org.mockito.Mockito.*; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class HostRequestsQuotaServiceTest { |
|||
|
|||
private HostRequestsQuotaService quotaService; |
|||
|
|||
private HostRequestIntervalRegistry requestRegistry = mock(HostRequestIntervalRegistry.class); |
|||
private HostRequestLimitPolicy requestsPolicy = mock(HostRequestLimitPolicy.class); |
|||
private HostIntervalRegistryCleaner registryCleaner = mock(HostIntervalRegistryCleaner.class); |
|||
private HostIntervalRegistryLogger registryLogger = mock(HostIntervalRegistryLogger.class); |
|||
|
|||
@Before |
|||
public void init() { |
|||
quotaService = new HostRequestsQuotaService(requestRegistry, requestsPolicy, registryCleaner, registryLogger, true); |
|||
} |
|||
|
|||
@Test |
|||
public void quotaExceededIfRequestCountBiggerThanAllowed() { |
|||
when(requestRegistry.tick("key")).thenReturn(10L); |
|||
when(requestsPolicy.isValid(10L)).thenReturn(false); |
|||
|
|||
assertTrue(quotaService.isQuotaExceeded("key")); |
|||
|
|||
verify(requestRegistry).tick("key"); |
|||
verify(requestsPolicy).isValid(10L); |
|||
verifyNoMoreInteractions(requestRegistry, requestsPolicy); |
|||
} |
|||
|
|||
@Test |
|||
public void quotaNotExceededIfRequestCountLessThanAllowed() { |
|||
when(requestRegistry.tick("key")).thenReturn(10L); |
|||
when(requestsPolicy.isValid(10L)).thenReturn(true); |
|||
|
|||
assertFalse(quotaService.isQuotaExceeded("key")); |
|||
|
|||
verify(requestRegistry).tick("key"); |
|||
verify(requestsPolicy).isValid(10L); |
|||
verifyNoMoreInteractions(requestRegistry, requestsPolicy); |
|||
} |
|||
|
|||
@Test |
|||
public void serviceCanBeDisabled() { |
|||
quotaService = new HostRequestsQuotaService(requestRegistry, requestsPolicy, registryCleaner, registryLogger, false); |
|||
assertFalse(quotaService.isQuotaExceeded("key")); |
|||
verifyNoMoreInteractions(requestRegistry, requestsPolicy); |
|||
} |
|||
} |
|||
@ -1,83 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.inmemory; |
|||
|
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.transport.quota.host.HostRequestIntervalRegistry; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class HostRequestIntervalRegistryTest { |
|||
|
|||
private HostRequestIntervalRegistry registry; |
|||
|
|||
@Before |
|||
public void init() { |
|||
registry = new HostRequestIntervalRegistry(10000L, 100L,"g1,g2", "b1"); |
|||
} |
|||
|
|||
@Test |
|||
public void newHostCreateNewInterval() { |
|||
assertEquals(1L, registry.tick("host1")); |
|||
} |
|||
|
|||
@Test |
|||
public void existingHostUpdated() { |
|||
registry.tick("aaa"); |
|||
assertEquals(1L, registry.tick("bbb")); |
|||
assertEquals(2L, registry.tick("aaa")); |
|||
} |
|||
|
|||
@Test |
|||
public void expiredIntervalsCleaned() throws InterruptedException { |
|||
registry.tick("aaa"); |
|||
Thread.sleep(150L); |
|||
registry.tick("bbb"); |
|||
registry.clean(); |
|||
assertEquals(1L, registry.tick("aaa")); |
|||
assertEquals(2L, registry.tick("bbb")); |
|||
} |
|||
|
|||
@Test |
|||
public void domainFromWhitelistNotCounted(){ |
|||
assertEquals(0L, registry.tick("g1")); |
|||
assertEquals(0L, registry.tick("g1")); |
|||
assertEquals(0L, registry.tick("g2")); |
|||
} |
|||
|
|||
@Test |
|||
public void domainFromBlackListReturnMaxValue(){ |
|||
assertEquals(Long.MAX_VALUE, registry.tick("b1")); |
|||
assertEquals(Long.MAX_VALUE, registry.tick("b1")); |
|||
} |
|||
|
|||
@Test |
|||
public void emptyWhitelistParsedOk(){ |
|||
registry = new HostRequestIntervalRegistry(10000L, 100L,"", "b1"); |
|||
assertEquals(1L, registry.tick("aaa")); |
|||
} |
|||
|
|||
@Test |
|||
public void emptyBlacklistParsedOk(){ |
|||
registry = new HostRequestIntervalRegistry(10000L, 100L,"", ""); |
|||
assertEquals(1L, registry.tick("aaa")); |
|||
} |
|||
} |
|||
@ -1,65 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.inmemory; |
|||
|
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.transport.quota.Clock; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class IntervalCountTest { |
|||
|
|||
@Before |
|||
public void init() { |
|||
Clock.setMillis(1000L); |
|||
} |
|||
|
|||
@After |
|||
public void clear() { |
|||
Clock.reset(); |
|||
} |
|||
|
|||
@Test |
|||
public void ticksInSameIntervalAreSummed() { |
|||
IntervalCount intervalCount = new IntervalCount(100L); |
|||
assertEquals(1L, intervalCount.resetIfExpiredAndTick()); |
|||
Clock.shift(100); |
|||
assertEquals(2L, intervalCount.resetIfExpiredAndTick()); |
|||
} |
|||
|
|||
@Test |
|||
public void oldDataCleanedWhenIntervalExpired() { |
|||
IntervalCount intervalCount = new IntervalCount(100L); |
|||
assertEquals(1L, intervalCount.resetIfExpiredAndTick()); |
|||
Clock.shift(101); |
|||
assertEquals(1L, intervalCount.resetIfExpiredAndTick()); |
|||
} |
|||
|
|||
@Test |
|||
public void silenceDurationCalculatedFromLastTick() { |
|||
IntervalCount intervalCount = new IntervalCount(100L); |
|||
assertEquals(1L, intervalCount.resetIfExpiredAndTick()); |
|||
Clock.shift(10L); |
|||
assertEquals(10L, intervalCount.silenceDuration()); |
|||
} |
|||
|
|||
} |
|||
@ -1,63 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.transport.quota.inmemory; |
|||
|
|||
import com.google.common.collect.ImmutableMap; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.transport.quota.host.HostIntervalRegistryLogger; |
|||
import org.thingsboard.server.common.transport.quota.host.HostRequestIntervalRegistry; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.Map; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.mockito.Mockito.mock; |
|||
|
|||
/** |
|||
* @author Vitaliy Paromskiy |
|||
* @version 1.0 |
|||
*/ |
|||
public class IntervalRegistryLoggerTest { |
|||
|
|||
private IntervalRegistryLogger logger; |
|||
|
|||
private HostRequestIntervalRegistry requestRegistry = mock(HostRequestIntervalRegistry.class); |
|||
|
|||
@Before |
|||
public void init() { |
|||
logger = new HostIntervalRegistryLogger(3, 10, requestRegistry); |
|||
} |
|||
|
|||
@Test |
|||
public void onlyMaxHostsCollected() { |
|||
Map<String, Long> map = ImmutableMap.of("a", 8L, "b", 3L, "c", 1L, "d", 3L); |
|||
Map<String, Long> actual = logger.getTopElements(map); |
|||
Map<String, Long> expected = ImmutableMap.of("a", 8L, "b", 3L, "d", 3L); |
|||
|
|||
assertEquals(expected, actual); |
|||
} |
|||
|
|||
@Test |
|||
public void emptyMapProcessedCorrectly() { |
|||
Map<String, Long> map = Collections.emptyMap(); |
|||
Map<String, Long> actual = logger.getTopElements(map); |
|||
Map<String, Long> expected = Collections.emptyMap(); |
|||
|
|||
assertEquals(expected, actual); |
|||
} |
|||
|
|||
} |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue