private IEnumerable <ProjectionInfo> GeneratedProjectionInfosFromProjectors(CancellationToken token, IDocumentStore memoryContainer, IDocumentStrategy strategy) { var trackingStore = new ProjectionInspectingStore(memoryContainer); var generatedProjectionInfos = new List <ProjectionInfo>(); foreach (var projection in this._projectors(trackingStore)) { var views = trackingStore.GetNewViewsAndReset(); var projectionInfo = new ProjectionInfo(views, projection, strategy); if (generatedProjectionInfos.Any(p => p.ProjectionName == projectionInfo.ProjectionName)) { throw new InvalidOperationException("Duplicate projection encountered: " + projectionInfo.ProjectionName); } generatedProjectionInfos.Add(projectionInfo); } trackingStore.ValidateSanity(); token.ThrowIfCancellationRequested(); if (trackingStore.Views.Count != generatedProjectionInfos.Count) { throw new InvalidOperationException("Projections count mismatch"); } return(generatedProjectionInfos); }
public static void Rebuild(CancellationToken token, IDocumentStore targetContainer, MessageStore stream, Func <IDocumentStore, IEnumerable <object> > projectors) { var strategy = targetContainer.Strategy; var memory = new MemoryStorageConfig(); var memoryContainer = memory.CreateNuclear(strategy).Container; var tracked = new ProjectionInspectingStore(memoryContainer); var projections = new List <object>(); projections.AddRange(projectors(tracked)); if (tracked.Projections.Count != projections.Count()) { throw new InvalidOperationException("Count mismatch"); } tracked.ValidateSanity(); var storage = new NuclearStorage(targetContainer); var persistedHashes = new Dictionary <string, string>(); var name = "domain"; storage.GetEntity <ProjectionHash>(name).IfValue(v => persistedHashes = v.BucketHashes); var activeMemoryProjections = projections.Select((projection, i) => { var proj = tracked.Projections[i]; var bucketName = proj.StoreBucket; var viewType = proj.EntityType; var projectionHash = "Global change on 2012-08-24\r\n" + GetClassHash(projection.GetType()) + "\r\n " + GetClassHash(viewType) + "\r\n" + GetClassHash(strategy.GetType()); bool needsRebuild = !persistedHashes.ContainsKey(bucketName) || persistedHashes[bucketName] != projectionHash; return(new { bucketName, projection, hash = projectionHash, needsRebuild }); }).ToArray(); foreach (var memoryProjection in activeMemoryProjections) { if (memoryProjection.needsRebuild) { SystemObserver.Notify("[warn] {0} needs rebuild", memoryProjection.bucketName); } else { SystemObserver.Notify("[good] {0} is up-to-date", memoryProjection.bucketName); } } var needRebuild = activeMemoryProjections.Where(x => x.needsRebuild).ToArray(); if (needRebuild.Length == 0) { return; } var watch = Stopwatch.StartNew(); var wire = new RedirectToDynamicEvent(); needRebuild.ForEach(x => wire.WireToWhen(x.projection)); var handlersWatch = Stopwatch.StartNew(); ObserveWhileCan(stream.EnumerateAllItems(0, int.MaxValue), wire, token); if (token.IsCancellationRequested) { SystemObserver.Notify("[warn] Aborting projections before anything was changed"); return; } var timeTotal = watch.Elapsed.TotalSeconds; var handlerTicks = handlersWatch.ElapsedTicks; var timeInHandlers = Math.Round(TimeSpan.FromTicks(handlerTicks).TotalSeconds, 1); SystemObserver.Notify("Total Elapsed: {0}sec ({1}sec in handlers)", Math.Round(timeTotal, 0), timeInHandlers); // update projections that need rebuild foreach (var b in needRebuild) { // server might shut down the process soon anyway, but we'll be // in partially consistent mode (not all projections updated) // so at least we blow up between projection buckets token.ThrowIfCancellationRequested(); var bucketName = b.bucketName; var bucketHash = b.hash; // wipe contents targetContainer.Reset(bucketName); // write new versions var contents = memoryContainer.EnumerateContents(bucketName); targetContainer.WriteContents(bucketName, contents); // update hash storage.UpdateEntityEnforcingNew <ProjectionHash>(name, x => { x.BucketHashes[bucketName] = bucketHash; }); SystemObserver.Notify("[good] Updated View bucket {0}.{1}", name, bucketName); } // Clean up obsolete views var allBuckets = new HashSet <string>(activeMemoryProjections.Select(p => p.bucketName)); var obsoleteBuckets = persistedHashes.Where(s => !allBuckets.Contains(s.Key)).ToArray(); foreach (var hash in obsoleteBuckets) { // quit at this stage without any bad side effects if (token.IsCancellationRequested) { return; } var bucketName = hash.Key; SystemObserver.Notify("[warn] {0} is obsolete", bucketName); targetContainer.Reset(bucketName); storage.UpdateEntityEnforcingNew <ProjectionHash>(name, x => x.BucketHashes.Remove(bucketName)); SystemObserver.Notify("[good] Cleaned up obsolete view bucket {0}.{1}", name, bucketName); } }