Browse Source
* added batch support to AttributeKvInsertRepository * added logs for testing * added batch support to AttributeKvInsertRepository * added logs for testing * Code review part 1 * Improvements * Batch Update Implementation * added realization SaveOrUpdate batch to Hsql and refactored * refactoredpull/2198/head
committed by
GitHub
9 changed files with 480 additions and 4 deletions
@ -0,0 +1,46 @@ |
|||
/** |
|||
* Copyright © 2016-2019 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.dao.sql; |
|||
|
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
import javax.annotation.PreDestroy; |
|||
import java.util.concurrent.Executors; |
|||
import java.util.concurrent.ScheduledExecutorService; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Component |
|||
public class ScheduledLogExecutorComponent { |
|||
|
|||
private ScheduledExecutorService schedulerLogExecutor; |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
schedulerLogExecutor = Executors.newSingleThreadScheduledExecutor(); |
|||
} |
|||
|
|||
@PreDestroy |
|||
public void stop() { |
|||
if (schedulerLogExecutor != null) { |
|||
schedulerLogExecutor.shutdownNow(); |
|||
} |
|||
} |
|||
|
|||
public void scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { |
|||
schedulerLogExecutor.scheduleAtFixedRate(command, initialDelay, period, unit); |
|||
} |
|||
} |
|||
@ -0,0 +1,114 @@ |
|||
/** |
|||
* Copyright © 2016-2019 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.dao.sql; |
|||
|
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import com.google.common.util.concurrent.SettableFuture; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.concurrent.BlockingQueue; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Executors; |
|||
import java.util.concurrent.LinkedBlockingQueue; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
import java.util.function.Consumer; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Slf4j |
|||
public class TbSqlBlockingQueue<E> implements TbSqlQueue<E> { |
|||
|
|||
private final BlockingQueue<TbSqlQueueElement<E>> queue = new LinkedBlockingQueue<>(); |
|||
private final AtomicInteger addedCount = new AtomicInteger(); |
|||
private final AtomicInteger savedCount = new AtomicInteger(); |
|||
private final AtomicInteger failedCount = new AtomicInteger(); |
|||
private final TbSqlBlockingQueueParams params; |
|||
|
|||
private ExecutorService executor; |
|||
private ScheduledLogExecutorComponent logExecutor; |
|||
|
|||
public TbSqlBlockingQueue(TbSqlBlockingQueueParams params) { |
|||
this.params = params; |
|||
} |
|||
|
|||
@Override |
|||
public void init(ScheduledLogExecutorComponent logExecutor, Consumer<List<E>> saveFunction) { |
|||
this.logExecutor = logExecutor; |
|||
executor = Executors.newSingleThreadExecutor(); |
|||
executor.submit(() -> { |
|||
String logName = params.getLogName(); |
|||
int batchSize = params.getBatchSize(); |
|||
long maxDelay = params.getMaxDelay(); |
|||
List<TbSqlQueueElement<E>> entities = new ArrayList<>(batchSize); |
|||
while (!Thread.interrupted()) { |
|||
try { |
|||
long currentTs = System.currentTimeMillis(); |
|||
TbSqlQueueElement<E> attr = queue.poll(maxDelay, TimeUnit.MILLISECONDS); |
|||
if (attr == null) { |
|||
continue; |
|||
} else { |
|||
entities.add(attr); |
|||
} |
|||
queue.drainTo(entities, batchSize - 1); |
|||
boolean fullPack = entities.size() == batchSize; |
|||
log.debug("[{}] Going to save {} entities", logName, entities.size()); |
|||
saveFunction.accept(entities.stream().map(TbSqlQueueElement::getEntity).collect(Collectors.toList())); |
|||
entities.forEach(v -> v.getFuture().set(null)); |
|||
savedCount.addAndGet(entities.size()); |
|||
if (!fullPack) { |
|||
long remainingDelay = maxDelay - (System.currentTimeMillis() - currentTs); |
|||
if (remainingDelay > 0) { |
|||
Thread.sleep(remainingDelay); |
|||
} |
|||
} |
|||
} catch (Exception e) { |
|||
failedCount.addAndGet(entities.size()); |
|||
entities.forEach(entityFutureWrapper -> entityFutureWrapper.getFuture().setException(e)); |
|||
if (e instanceof InterruptedException) { |
|||
log.info("[{}] Queue polling was interrupted", logName); |
|||
break; |
|||
} else { |
|||
log.error("[{}] Failed to save {} entities", logName, entities.size(), e); |
|||
} |
|||
} finally { |
|||
entities.clear(); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
logExecutor.scheduleAtFixedRate(() -> { |
|||
log.info("Attributes queueSize [{}] totalAdded [{}] totalSaved [{}] totalFailed [{}]", |
|||
queue.size(), addedCount.getAndSet(0), savedCount.getAndSet(0), failedCount.getAndSet(0)); |
|||
}, params.getStatsPrintIntervalMs(), params.getStatsPrintIntervalMs(), TimeUnit.MILLISECONDS); |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
if (executor != null) { |
|||
executor.shutdownNow(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public ListenableFuture<Void> add(E element) { |
|||
SettableFuture<Void> future = SettableFuture.create(); |
|||
queue.add(new TbSqlQueueElement<>(future, element)); |
|||
addedCount.incrementAndGet(); |
|||
return future; |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* Copyright © 2016-2019 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.dao.sql; |
|||
|
|||
import lombok.Builder; |
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
@Slf4j |
|||
@Data |
|||
@Builder |
|||
public class TbSqlBlockingQueueParams { |
|||
|
|||
private final String logName; |
|||
private final int batchSize; |
|||
private final long maxDelay; |
|||
private final long statsPrintIntervalMs; |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/** |
|||
* Copyright © 2016-2019 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.dao.sql; |
|||
|
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
|
|||
import java.util.List; |
|||
import java.util.function.Consumer; |
|||
|
|||
public interface TbSqlQueue<E> { |
|||
|
|||
void init(ScheduledLogExecutorComponent logExecutor, Consumer<List<E>> saveFunction); |
|||
|
|||
void destroy(); |
|||
|
|||
ListenableFuture<Void> add(E element); |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2019 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.dao.sql; |
|||
|
|||
import com.google.common.util.concurrent.SettableFuture; |
|||
import lombok.Getter; |
|||
|
|||
public final class TbSqlQueueElement<E> { |
|||
@Getter |
|||
private final SettableFuture<Void> future; |
|||
@Getter |
|||
private final E entity; |
|||
|
|||
public TbSqlQueueElement(SettableFuture<Void> future, E entity) { |
|||
this.future = future; |
|||
this.entity = entity; |
|||
} |
|||
} |
|||
|
|||
|
|||
Loading…
Reference in new issue