7 changed files with 217 additions and 56 deletions
@ -0,0 +1,105 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.service.apiusage; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.common.data.ApiUsageRecordKey; |
||||
|
import org.thingsboard.server.common.data.ApiUsageState; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
||||
|
import org.thingsboard.server.common.data.kv.LongDataEntry; |
||||
|
import org.thingsboard.server.common.data.kv.TsKvEntry; |
||||
|
import org.thingsboard.server.common.msg.queue.TbCallback; |
||||
|
import org.thingsboard.server.common.msg.tools.SchedulerUtils; |
||||
|
import org.thingsboard.server.dao.timeseries.TimeseriesService; |
||||
|
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos.UsageStatsKVProto; |
||||
|
import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
import java.time.LocalDate; |
||||
|
import java.time.ZoneId; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
import java.util.concurrent.ExecutionException; |
||||
|
|
||||
|
@Slf4j |
||||
|
@TbCoreComponent |
||||
|
@Service |
||||
|
public class DefaultTbApiUsageStateService implements TbApiUsageStateService { |
||||
|
|
||||
|
private final ApiUsageStateService apiUsageStateService; |
||||
|
private final TimeseriesService tsService; |
||||
|
private final ZoneId zoneId; |
||||
|
private final Map<TenantId, TenantApiUsageState> tenantStates = new ConcurrentHashMap<>(); |
||||
|
|
||||
|
public DefaultTbApiUsageStateService(ApiUsageStateService apiUsageStateService, TimeseriesService tsService) { |
||||
|
this.apiUsageStateService = apiUsageStateService; |
||||
|
this.tsService = tsService; |
||||
|
this.zoneId = SchedulerUtils.getZoneId("UTC"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void process(TbProtoQueueMsg<ToUsageStatsServiceMsg> msg, TbCallback callback) { |
||||
|
ToUsageStatsServiceMsg statsMsg = msg.getValue(); |
||||
|
TenantId tenantId = new TenantId(new UUID(statsMsg.getTenantIdMSB(), statsMsg.getTenantIdLSB())); |
||||
|
TenantApiUsageState tenantState = getOrFetchState(tenantId); |
||||
|
long ts = tenantState.getCurrentMonthTs(); |
||||
|
List<TsKvEntry> updatedEntries = new ArrayList<>(ApiUsageRecordKey.values().length); |
||||
|
for (UsageStatsKVProto kvProto : statsMsg.getValuesList()) { |
||||
|
ApiUsageRecordKey recordKey = ApiUsageRecordKey.valueOf(kvProto.getKey()); |
||||
|
long newValue = tenantState.add(recordKey, kvProto.getValue()); |
||||
|
updatedEntries.add(new BasicTsKvEntry(ts, new LongDataEntry(recordKey.name(), newValue))); |
||||
|
} |
||||
|
tsService.save(tenantId, tenantState.getEntityId(), updatedEntries, 0L); |
||||
|
} |
||||
|
|
||||
|
private TenantApiUsageState getOrFetchState(TenantId tenantId) { |
||||
|
TenantApiUsageState tenantState = tenantStates.get(tenantId); |
||||
|
if (tenantState == null) { |
||||
|
long currentMonthTs = LocalDate.now().withDayOfMonth(1).atStartOfDay(zoneId).toInstant().toEpochMilli(); |
||||
|
ApiUsageState dbStateEntity = apiUsageStateService.findTenantApiUsageState(tenantId); |
||||
|
tenantState = new TenantApiUsageState(currentMonthTs, dbStateEntity.getEntityId()); |
||||
|
try { |
||||
|
List<TsKvEntry> dbValues = tsService.findAllLatest(tenantId, dbStateEntity.getEntityId()).get(); |
||||
|
for (ApiUsageRecordKey key : ApiUsageRecordKey.values()) { |
||||
|
TsKvEntry keyEntry = null; |
||||
|
for (TsKvEntry tsKvEntry : dbValues) { |
||||
|
if (tsKvEntry.getKey().equals(key.name())) { |
||||
|
keyEntry = tsKvEntry; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (keyEntry != null) { |
||||
|
tenantState.put(key, keyEntry.getLongValue().get()); |
||||
|
} else { |
||||
|
tenantState.put(key, 0L); |
||||
|
} |
||||
|
} |
||||
|
tenantStates.put(tenantId, tenantState); |
||||
|
} catch (InterruptedException | ExecutionException e) { |
||||
|
log.warn("[{}] Failed to fetch api usage state from db.", tenantId, e); |
||||
|
} |
||||
|
} |
||||
|
return tenantState; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -1,30 +0,0 @@ |
|||||
/** |
|
||||
* Copyright © 2016-2020 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.service.apiusage; |
|
||||
|
|
||||
import org.springframework.stereotype.Service; |
|
||||
import org.thingsboard.server.common.msg.queue.TbCallback; |
|
||||
import org.thingsboard.server.gen.transport.TransportProtos; |
|
||||
import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
|
||||
|
|
||||
@Service |
|
||||
public class DefaultTbUsageStatsService implements TbUsageStatsService { |
|
||||
@Override |
|
||||
public void process(TbProtoQueueMsg<TransportProtos.ToUsageStatsServiceMsg> msg, TbCallback callback) { |
|
||||
|
|
||||
} |
|
||||
|
|
||||
} |
|
||||
@ -0,0 +1,47 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.service.apiusage; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import org.thingsboard.server.common.data.ApiUsageRecordKey; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
|
||||
|
import java.util.Map; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
|
||||
|
public class TenantApiUsageState { |
||||
|
|
||||
|
private final Map<ApiUsageRecordKey, Long> values = new ConcurrentHashMap<>(); |
||||
|
@Getter |
||||
|
private final EntityId entityId; |
||||
|
@Getter |
||||
|
private volatile long currentMonthTs; |
||||
|
|
||||
|
public TenantApiUsageState(long currentMonthTs, EntityId entityId) { |
||||
|
this.entityId = entityId; |
||||
|
this.currentMonthTs = currentMonthTs; |
||||
|
} |
||||
|
|
||||
|
public void put(ApiUsageRecordKey key, Long value) { |
||||
|
values.put(key, value); |
||||
|
} |
||||
|
|
||||
|
public long add(ApiUsageRecordKey key, long value) { |
||||
|
long result = values.getOrDefault(key, 0L) + value; |
||||
|
values.put(key, result); |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue