10 changed files with 224 additions and 119 deletions
@ -0,0 +1,94 @@ |
|||
/** |
|||
* Copyright © 2016-2025 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.edqs.stats; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.ObjectType; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.query.EntityCountQuery; |
|||
import org.thingsboard.server.common.data.query.EntityDataQuery; |
|||
import org.thingsboard.server.common.stats.EdqsStatsService; |
|||
import org.thingsboard.server.common.stats.StatsFactory; |
|||
import org.thingsboard.server.common.stats.StatsTimer; |
|||
import org.thingsboard.server.common.stats.StatsType; |
|||
import org.thingsboard.server.queue.edqs.EdqsComponent; |
|||
|
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
|
|||
@EdqsComponent |
|||
@Service |
|||
@Slf4j |
|||
@ConditionalOnProperty(name = "queue.edqs.stats.enabled", havingValue = "true", matchIfMissing = true) |
|||
public class DefaultEdqsStatsService implements EdqsStatsService { |
|||
|
|||
private final StatsFactory statsFactory; |
|||
|
|||
@Value("${queue.edqs.stats.slow_query_threshold:3000}") |
|||
private int slowQueryThreshold; |
|||
|
|||
private final ConcurrentHashMap<ObjectType, AtomicInteger> objectCounters = new ConcurrentHashMap<>(); |
|||
private final StatsTimer dataQueryTimer; |
|||
private final StatsTimer countQueryTimer; |
|||
|
|||
private DefaultEdqsStatsService(StatsFactory statsFactory) { |
|||
this.statsFactory = statsFactory; |
|||
dataQueryTimer = statsFactory.createTimer(StatsType.EDQS, "entityDataQueryTimer"); |
|||
countQueryTimer = statsFactory.createTimer(StatsType.EDQS, "entityCountQueryTimer"); |
|||
} |
|||
|
|||
@Override |
|||
public void reportAdded(ObjectType objectType) { |
|||
getObjectCounter(objectType).incrementAndGet(); |
|||
} |
|||
|
|||
@Override |
|||
public void reportRemoved(ObjectType objectType) { |
|||
getObjectCounter(objectType).decrementAndGet(); |
|||
} |
|||
|
|||
@Override |
|||
public void reportDataQuery(TenantId tenantId, EntityDataQuery query, long timingNanos) { |
|||
double timingMs = timingNanos / 1000_000.0; |
|||
if (timingMs < slowQueryThreshold) { |
|||
log.info("[{}] Executed data query in {} ms: {}", tenantId, timingMs, query); |
|||
} else { |
|||
log.warn("[{}] Executed slow data query in {} ms: {}", tenantId, timingMs, query); |
|||
} |
|||
dataQueryTimer.record(timingNanos, TimeUnit.NANOSECONDS); |
|||
} |
|||
|
|||
@Override |
|||
public void reportCountQuery(TenantId tenantId, EntityCountQuery query, long timingNanos) { |
|||
double timingMs = timingNanos / 1000_000.0; |
|||
if (timingMs < slowQueryThreshold) { |
|||
log.info("[{}] Executed count query in {} ms: {}", tenantId, timingMs, query); |
|||
} else { |
|||
log.warn("[{}] Executed slow count query in {} ms: {}", tenantId, timingMs, query); |
|||
} |
|||
countQueryTimer.record(timingNanos, TimeUnit.NANOSECONDS); |
|||
} |
|||
|
|||
private AtomicInteger getObjectCounter(ObjectType objectType) { |
|||
return objectCounters.computeIfAbsent(objectType, type -> |
|||
statsFactory.createGauge("edqsObjectsCount", new AtomicInteger(), "objectType", type.name())); |
|||
} |
|||
|
|||
} |
|||
@ -1,81 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2025 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.edqs.stats; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.ObjectType; |
|||
import org.thingsboard.server.common.data.edqs.EdqsEventType; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.stats.StatsFactory; |
|||
import org.thingsboard.server.common.stats.StatsType; |
|||
import org.thingsboard.server.queue.edqs.EdqsComponent; |
|||
|
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@EdqsComponent |
|||
@Service |
|||
@Slf4j |
|||
@RequiredArgsConstructor |
|||
@ConditionalOnProperty(name = "queue.edqs.stats.enabled", havingValue = "true", matchIfMissing = true) |
|||
public class EdqsStatsService { |
|||
|
|||
private final ConcurrentHashMap<TenantId, EdqsStats> statsMap = new ConcurrentHashMap<>(); |
|||
private final StatsFactory statsFactory; |
|||
|
|||
public void reportEvent(TenantId tenantId, ObjectType objectType, EdqsEventType eventType) { |
|||
statsMap.computeIfAbsent(tenantId, id -> new EdqsStats(tenantId, statsFactory)) |
|||
.reportEvent(objectType, eventType); |
|||
} |
|||
|
|||
@Getter |
|||
@AllArgsConstructor |
|||
static class EdqsStats { |
|||
|
|||
private final TenantId tenantId; |
|||
private final ConcurrentHashMap<ObjectType, AtomicInteger> entityCounters = new ConcurrentHashMap<>(); |
|||
private final StatsFactory statsFactory; |
|||
|
|||
private AtomicInteger getOrCreateObjectCounter(ObjectType objectType) { |
|||
return entityCounters.computeIfAbsent(objectType, |
|||
type -> statsFactory.createGauge(StatsType.EDQS.getName() + "_object_count", new AtomicInteger(), |
|||
"tenantId", tenantId.toString(), "objectType", type.name())); |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
return entityCounters.entrySet().stream() |
|||
.map(counters -> counters.getKey().name()+ " total = [" + counters.getValue() + "]") |
|||
.collect(Collectors.joining(", ")); |
|||
} |
|||
|
|||
public void reportEvent(ObjectType objectType, EdqsEventType eventType) { |
|||
AtomicInteger objectCounter = getOrCreateObjectCounter(objectType); |
|||
if (eventType == EdqsEventType.UPDATED){ |
|||
objectCounter.incrementAndGet(); |
|||
} else if (eventType == EdqsEventType.DELETED) { |
|||
objectCounter.decrementAndGet(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* Copyright © 2016-2025 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.stats; |
|||
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.ObjectType; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.query.EntityCountQuery; |
|||
import org.thingsboard.server.common.data.query.EntityDataQuery; |
|||
|
|||
@Service |
|||
@ConditionalOnMissingBean(value = EdqsStatsService.class, ignored = DummyEdqsStatsService.class) |
|||
public class DummyEdqsStatsService implements EdqsStatsService { |
|||
|
|||
@Override |
|||
public void reportAdded(ObjectType objectType) {} |
|||
|
|||
@Override |
|||
public void reportRemoved(ObjectType objectType) {} |
|||
|
|||
@Override |
|||
public void reportDataQuery(TenantId tenantId, EntityDataQuery query, long timingNanos) {} |
|||
|
|||
@Override |
|||
public void reportCountQuery(TenantId tenantId, EntityCountQuery query, long timingNanos) {} |
|||
|
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2025 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.stats; |
|||
|
|||
import org.thingsboard.server.common.data.ObjectType; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.query.EntityCountQuery; |
|||
import org.thingsboard.server.common.data.query.EntityDataQuery; |
|||
|
|||
public interface EdqsStatsService { |
|||
|
|||
void reportAdded(ObjectType objectType); |
|||
|
|||
void reportRemoved(ObjectType objectType); |
|||
|
|||
void reportDataQuery(TenantId tenantId, EntityDataQuery query, long timingNanos); |
|||
|
|||
void reportCountQuery(TenantId tenantId, EntityCountQuery query, long timingNanos); |
|||
|
|||
} |
|||
Loading…
Reference in new issue