public SegmentCacheManager(MondrianServer server) { this.server = server; ACTOR = new Actor(); thread = new Thread(ACTOR, "mondrian.rolap.agg.SegmentCacheManager$ACTOR"); thread.setDaemon(true); thread.start(); // Create the index registry. this.indexRegistry = new SegmentCacheIndexRegistry(); // Add a local cache, if needed. if (!MondrianProperties.instance().DisableCaching.get()) { final MemorySegmentCache cache = new MemorySegmentCache(); segmentCacheWorkers.add(new SegmentCacheWorker(cache, thread)); } // Add an external cache, if configured. final List<SegmentCache> externalCache = SegmentCacheWorker.initCache(); for (SegmentCache cache : externalCache) { // Create a worker for this external cache segmentCacheWorkers.add(new SegmentCacheWorker(cache, thread)); // Hook up a listener so it can update // the segment index. cache.addListener(new AsyncCacheListener(this, server)); } compositeCache = new CompositeSegmentCache(segmentCacheWorkers); }
public List<SegmentHeader> getSegmentHeaders() { // Special case 0 and 1 workers, for which the 'union' operation // is trivial. switch (workers.size()) { case 0: return Collections.emptyList(); case 1: return workers.get(0).getSegmentHeaders(); default: final List<SegmentHeader> list = new ArrayList<SegmentHeader>(); final Set<SegmentHeader> set = new HashSet<SegmentHeader>(); for (SegmentCacheWorker worker : workers) { for (SegmentHeader header : worker.getSegmentHeaders()) { if (set.add(header)) { list.add(header); } } } return list; } }
public FlushResult call() throws Exception { // For each measure and each star, ask the index // which headers intersect. final List<SegmentHeader> headers = new ArrayList<SegmentHeader>(); final List<Member> measures = CacheControlImpl.findMeasures(region); final SegmentColumn[] flushRegion = CacheControlImpl.findAxisValues(region); final List<RolapStar> starList = CacheControlImpl.getStarList(region); for (Member member : measures) { if (!(member instanceof RolapStoredMeasure)) { continue; } final RolapStoredMeasure storedMeasure = (RolapStoredMeasure) member; final RolapStar star = storedMeasure.getCube().getStar(); final SegmentCacheIndex index = cacheMgr.indexRegistry.getIndex(star); headers.addAll( index.intersectRegion( member.getDimension().getSchema().getName(), ((RolapSchema) member.getDimension().getSchema()).getChecksum(), storedMeasure.getCube().getName(), storedMeasure.getName(), storedMeasure.getCube().getStar().getFactTable().getAlias(), flushRegion)); } // If flushRegion is empty, this means we must clear all // segments for the region's measures. if (flushRegion.length == 0) { for (final SegmentHeader header : headers) { for (RolapStar star : starList) { cacheMgr.indexRegistry.getIndex(star).remove(header); } // Remove the segment from external caches. Use an // executor, because it may take some time. We discard // the future, because we don't care too much if it fails. cacheControlImpl.trace( "discard segment - it cannot be constrained and maintain consistency:\n" + header.getDescription()); final Future<?> task = cacheMgr.cacheExecutor.submit( new Runnable() { public void run() { try { // Note that the SegmentCache API doesn't // require us to verify that the segment // exists (by calling "contains") before we // call "remove". cacheMgr.compositeCache.remove(header); } catch (Throwable e) { LOGGER.warn("remove header failed: " + header, e); } } }); Util.safeGet(task, "SegmentCacheManager.flush"); } return new FlushResult(Collections.<Callable<Boolean>>emptyList()); } // Now we know which headers intersect. For each of them, // we append an excluded region. // // TODO: Optimize the logic here. If a segment is mostly // empty, we should trash it completely. final List<Callable<Boolean>> callableList = new ArrayList<Callable<Boolean>>(); for (final SegmentHeader header : headers) { if (!header.canConstrain(flushRegion)) { // We have to delete that segment altogether. cacheControlImpl.trace( "discard segment - it cannot be constrained and maintain consistency:\n" + header.getDescription()); for (RolapStar star : starList) { cacheMgr.indexRegistry.getIndex(star).remove(header); } continue; } final SegmentHeader newHeader = header.constrain(flushRegion); for (final SegmentCacheWorker worker : cacheMgr.segmentCacheWorkers) { callableList.add( new Callable<Boolean>() { public Boolean call() throws Exception { boolean existed; if (worker.supportsRichIndex()) { final SegmentBody sb = worker.get(header); existed = worker.remove(header); if (sb != null) { worker.put(newHeader, sb); } } else { // The cache doesn't support rich index. We // have to clear the segment entirely. existed = worker.remove(header); } return existed; } }); } for (RolapStar star : starList) { SegmentCacheIndex index = cacheMgr.indexRegistry.getIndex(star); index.remove(header); index.add(newHeader, false, null); } } // Done return new FlushResult(callableList); }