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