/** * Copyright (c) Codice Foundation * <p> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package ddf.catalog.cache.solr.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; import ddf.catalog.data.Metacard; import ddf.catalog.data.Result; /** * Bulk adds metacards to the cache that are not needed immediately. */ class CacheBulkProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(CacheBulkProcessor.class); private final ScheduledExecutorService batchScheduler = Executors.newSingleThreadScheduledExecutor(); private final Map<String, Metacard> metacardsToCache = new ConcurrentHashMap<>(); private long flushInterval = TimeUnit.SECONDS.toMillis(10); private int maximumBacklogSize = 10000; private int batchSize = 500; private Date lastBulkAdd = new Date(); public CacheBulkProcessor(final SolrCache cache) { this(cache, 1, TimeUnit.SECONDS); } /** * Create a new cache bulk processor that will check added metacards for bulk processing at * the configured delay interval. * * @param cache target Solr cache to bulk add metacards * @param delay delay between decision to bulk add * @param delayUnit units of the delay */ public CacheBulkProcessor(final SolrCache cache, final long delay, final TimeUnit delayUnit) { batchScheduler.scheduleWithFixedDelay(new Runnable() { @Override public void run() { try { if (metacardsToCache.size() > 0 && (metacardsToCache.size() >= batchSize || timeToFlush())) { LOGGER.debug("{} metacards to batch add to cache", metacardsToCache.size()); List<Metacard> metacards = new ArrayList<>(metacardsToCache.values()); for (Collection<Metacard> batch : Lists.partition(metacards, batchSize)) { LOGGER.debug("Caching a batch of {} metacards", batch.size()); cache.create(batch); for (Metacard metacard : batch) { metacardsToCache.remove(metacard.getId()); } } lastBulkAdd = new Date(); } } catch (Throwable throwable) { LOGGER.warn("Scheduled bulk ingest to cache failed", throwable); } } }, delay, delay, delayUnit); } private boolean timeToFlush() { Date now = new Date(); return now.getTime() - lastBulkAdd.getTime() > flushInterval; } /** * Adds metacards to be bulk added to cache. Metacards will be ignored if backlog grows too * large. Metacard currently in backlog will be updated if added again. * * @param results metacards to add to current batch */ public void add(final List<Result> results) { if (metacardsToCache.size() < maximumBacklogSize) { for (Result result : results) { if (result != null) { Metacard metacard = result.getMetacard(); if (metacard != null) { metacardsToCache.put(metacard.getId(), metacard); } } } } } /** * Shutdown scheduled tasks. */ public void shutdown() { batchScheduler.shutdown(); } int pendingMetacards() { return metacardsToCache.size(); } public void setFlushInterval(long flushInterval) { this.flushInterval = flushInterval; } public void setBatchSize(int batchSize) { this.batchSize = batchSize; } public void setMaximumBacklogSize(int maximumBacklogSize) { this.maximumBacklogSize = maximumBacklogSize; } }