public async Task DurableCache_EventsAreSavedAndRestored() { // Create a test event to add to the cache var testEvent = new CachedEvent("CollectionName", JObject.FromObject(new { Property = "Value" })); // Create the cache to be tested var cache = await EventCachePortableTestable.NewTestableAsync(); // Add the event to the cache await cache.AddAsync(testEvent); // Destroy the cache object and clear the static event queue cache.ResetStaticMembers(); cache = null; // The event should have been written to disk, and creating a new cache should populate // from disk var newCache = await EventCachePortableTestable.NewTestableAsync(); var actualEvent = await newCache.TryTakeAsync(); // Event read should be equal to the original Assert.NotNull(actualEvent); Assert.AreEqual(testEvent.Event, actualEvent.Event); Assert.AreEqual(testEvent.Collection, actualEvent.Collection); Assert.AreEqual(testEvent.Error, actualEvent.Error); // Shouldn't be more events stored Assert.Null(await newCache.TryTakeAsync()); }
public async Task <CachedEvent> TryTake() { var keenFolder = await getKeenFolder() .ConfigureAwait(continueOnCapturedContext: false); if (!events.Any()) { return(null); } string fileName; lock (events) fileName = events.Dequeue(); var file = await keenFolder.GetFileAsync(fileName) .ConfigureAwait(continueOnCapturedContext: false); var content = await file.ReadAllTextAsync() .ConfigureAwait(continueOnCapturedContext: false); dynamic ce = JObject.Parse(content); var item = new CachedEvent((string)ce.Collection, (JObject)ce.Event, (Exception)ce.Error); await file.DeleteAsync() .ConfigureAwait(continueOnCapturedContext: false); return(item); }
public ActionResult ViewImage(int id, long? @event, long?user) { CachedEvent viewEvent = null; if (@event.HasValue) { viewEvent = this.Events.GetEvent(@event.Value); if (viewEvent == null) { return(this.HttpError(404, this.View("NotFound"))); } } UserInformation viewUser = null; if (user.HasValue) { viewUser = this.Users.GetUserInformation(user.Value); if (viewUser == null) { return(this.HttpError(404, this.View("NotFound"))); } } var images = FilterImages(this.Db.EventImages, @event, user); var imageCount = images.Count(); if (imageCount == 0) { return(this.HttpError(404, this.View("NotFound"))); } var viewImages = images.ToList(); id = Math.Max(1, Math.Min(imageCount, id)); var viewImage = viewImages.Skip(id - 1).Take(1).SingleOrDefault(); var tags = (from t in this.Db.EventImageUserTags where t.EventImageId == viewImage.EventImageID select new UserTag { UserId = t.TaggedUserUserId, Username = t.TaggedUser.Username, TagRegion = t.RegionGeometry }).ToList(); return(this.View(new ViewImageModel { Event = viewEvent, User = viewUser, Image = viewImage, Images = viewImages, UserTags = tags, AllowTagging = this.Security.IsUserAdministrator(this.CurrentUser) })); }
public ActionResult Gallery(long? @event, long?user, int?page) { int pageSize = 50; CachedEvent viewEvent = null; if (@event.HasValue) { viewEvent = this.Events.GetEvent(@event.Value); if (viewEvent == null) { return(this.HttpError(404, this.View("NotFound"))); } } UserInformation viewUser = null; if (user.HasValue) { viewUser = this.Users.GetUserInformation(user.Value); if (viewUser == null) { return(this.HttpError(404, this.View("NotFound"))); } } var images = FilterImages(this.Db.EventImages, @event, user); int imageCount = images.Count(); int pages = Pager.PageCount(imageCount, pageSize); page = Pager.ClampPage(page, pages); var viewImages = images.Skip((page.Value - 1) * pageSize).Take(pageSize).ToList(); return(this.View(new ImageGalleryModel { Event = viewEvent, User = viewUser, Images = viewImages, PageInfo = new PaginationInformation { Pager = this.Skins.GetDefaultGalleryPager(), CurrentPage = page, Items = imageCount, ItemsPerPage = pageSize, ControllerName = "Images", ActionName = "Gallery", PageAttribute = "page", RouteValues = new System.Web.Routing.RouteValueDictionary(new { @event = @event, user = user }) } })); }
public Task Add(CachedEvent e) { if (null == e) throw new KeenException("Cached events may not be null"); return Task.Run(() => { lock (events) events.Enqueue(e); }); }
public async Task <CachedEvent> TryTakeAsync() { if (!events.Any()) { return(null); } string fileName; lock (events) { // Get the file name of the first event in the queue fileName = events.First(); } string fullFileName = Path.Combine(GetKeenFolderPath(), fileName); CachedEvent item; try { string content; using (FileStream stream = File.Open(fullFileName, FileMode.Open)) { byte[] fileBytes = new byte[stream.Length]; await stream.ReadAsync(fileBytes, 0, (int)stream.Length); content = Encoding.UTF8.GetString(fileBytes); } var ce = JObject.Parse(content); item = new CachedEvent( (string)ce.SelectToken("Collection"), (JObject)ce.SelectToken("Event"), ce.SelectToken("Error").ToObject <Exception>()); await Task.Run(() => File.Delete(fullFileName)) .ConfigureAwait(continueOnCapturedContext: false); } finally { lock (events) { // Dequeue the event now that we're done with it events.Dequeue(); } } return(item); }
/// <summary> /// Adds an event to the cache. /// </summary> /// <returns></returns> /// <param name="e">The CachedEvent to add to the cache.</param> public async Task AddAsync(CachedEvent e) { if (null == e) { throw new KeenException("Cached events may not be null"); } // Get/create the cache directory DirectoryInfo keenFolder = await GetOrCreateKeenDirectoryAsync() .ConfigureAwait(continueOnCapturedContext: false); // Come up with a name to use for the file on disk. // This is sufficiently random such that name collisions // won't happen. string fileName = Path.GetRandomFileName(); lock (events) { events.Enqueue(fileName); } try { var content = JObject.FromObject(e).ToString(); using (FileStream stream = File.Open(Path.Combine(keenFolder.FullName, fileName), FileMode.CreateNew)) { byte[] fileBytes = Encoding.UTF8.GetBytes(content); await stream.WriteAsync(fileBytes, 0, fileBytes.Length); } } catch (Exception ex) { throw new KeenException("Failure while saving file", ex); } }
public async Task Add(CachedEvent e) { if (null == e) { throw new KeenException("Cached events may not be null"); } var keenFolder = await getKeenFolder() .ConfigureAwait(continueOnCapturedContext: false); IFile file; var attempts = 0; var done = false; string name = null; do { attempts++; // Avoid race conditions in parallel environment by locking on the events queue // and generating and inserting a unique name within the lock. CreateFileAsync has // a CreateCollisionOption.GenerateUniqueName, but it will return the same name // multiple times when called from parallel tasks. // If creating and writing the file fails, loop around and if (string.IsNullOrEmpty(name)) { lock (events) { var i = 0; while (events.Contains(name = e.Collection + i++)) { ; } events.Enqueue(name); } } Exception lastErr = null; try { file = await keenFolder.CreateFileAsync(name, CreationCollisionOption.FailIfExists) .ConfigureAwait(continueOnCapturedContext: false); var content = JObject.FromObject(e).ToString(); await file.WriteAllTextAsync(content) .ConfigureAwait(continueOnCapturedContext: false); done = true; } catch (Exception ex) { lastErr = ex; } // If the file was not created, not written, or partially written, // the events queue may be left with a file name that references a // file that is nonexistent, empty, or invalid. It's easier to handle // this when the queue is read than to try to dequeue the name. if (attempts > 100) { throw new KeenException("Persistent failure while saving file, aborting", lastErr); } } while (!done); }