public void Cleanup(string db, bool skipIfActive, Func <TResource, bool> shouldSkip = null) { using (ResourcesStoresCache.WithAllLocks()) { DateTime time; Task <TResource> databaseTask; if (ResourcesStoresCache.TryGetValue(db, out databaseTask) == false) { LastRecentlyUsed.TryRemove(db, out time); return; } if (databaseTask.Status == TaskStatus.Faulted || databaseTask.Status == TaskStatus.Canceled) { LastRecentlyUsed.TryRemove(db, out time); ResourcesStoresCache.TryRemove(db, out databaseTask); return; } if (databaseTask.Status != TaskStatus.RanToCompletion) { return; // still starting up } var database = databaseTask.Result; if ((skipIfActive && (SystemTime.UtcNow - LastWork(database)).TotalMinutes < 10) || (shouldSkip != null && shouldSkip(database))) { // this document might not be actively working with user, but it is actively doing indexes, we will // wait with unloading this database until it hasn't done indexing for a while. // This prevent us from shutting down big databases that have been left alone to do indexing work. return; } try { database.Dispose(); } catch (Exception e) { Logger.ErrorException("Could not cleanup tenant database: " + db, e); return; } LastRecentlyUsed.TryRemove(db, out time); ResourcesStoresCache.TryRemove(db, out databaseTask); var onDatabaseCleanupOccured = CleanupOccured; if (onDatabaseCleanupOccured != null) { onDatabaseCleanupOccured(db); } } }
public void Cleanup(string resource, TimeSpan?skipIfActiveInDuration, Func <TResource, bool> shouldSkip = null, DocumentChangeTypes notificationType = DocumentChangeTypes.None) { if (Cleanups.TryAdd(resource, new ManualResetEvent(false)) == false) { return; } try { using (ResourcesStoresCache.WithAllLocks()) { DateTime time; Task <TResource> resourceTask; if (ResourcesStoresCache.TryGetValue(resource, out resourceTask) == false) { LastRecentlyUsed.TryRemove(resource, out time); return; } if (resourceTask.Status == TaskStatus.Faulted || resourceTask.Status == TaskStatus.Canceled) { LastRecentlyUsed.TryRemove(resource, out time); ResourcesStoresCache.TryRemove(resource, out resourceTask); return; } if (resourceTask.Status != TaskStatus.RanToCompletion) { return; // still starting up } var database = resourceTask.Result; if ((skipIfActiveInDuration != null && (SystemTime.UtcNow - LastWork(database)) < skipIfActiveInDuration) || (shouldSkip != null && shouldSkip(database))) { // this document might not be actively working with user, but it is actively doing indexes, we will // wait with unloading this database until it hasn't done indexing for a while. // This prevent us from shutting down big databases that have been left alone to do indexing work. return; } try { database.Dispose(); } catch (Exception e) { Logger.ErrorException("Could not cleanup tenant database: " + resource, e); return; } LastRecentlyUsed.TryRemove(resource, out time); ResourcesStoresCache.TryRemove(resource, out resourceTask); if (notificationType == DocumentChangeTypes.Delete) { TransportState transportState; ResourseTransportStates.TryRemove(resource, out transportState); if (transportState != null) { transportState.Dispose(); } } var onResourceCleanupOccured = CleanupOccured; if (onResourceCleanupOccured != null) { onResourceCleanupOccured(resource); } } } finally { ManualResetEvent cleanupLock; if (Cleanups.TryRemove(resource, out cleanupLock)) { cleanupLock.Set(); } } }
public void Dispose() { disposerLock.EnterWriteLock(); try { TenantDatabaseModified.Occured -= TenantDatabaseRemoved; var exceptionAggregator = new ExceptionAggregator(logger, "Could not properly dispose of HttpServer"); exceptionAggregator.Execute(() => { if (serverTimer != null) { serverTimer.Dispose(); } }); exceptionAggregator.Execute(() => { if (listener != null && listener.IsListening) { listener.Stop(); } }); disposed = true; exceptionAggregator.Execute(() => { using (ResourcesStoresCache.WithAllLocks()) { // shut down all databases in parallel, avoid having to wait for each one Parallel.ForEach(ResourcesStoresCache.Values, dbTask => { if (dbTask.IsCompleted == false) { dbTask.ContinueWith(task => { if (task.Status != TaskStatus.RanToCompletion) { return; } try { task.Result.Dispose(); } catch (Exception e) { logger.WarnException("Failure in deferred disosal of a database", e); } }); } else if (dbTask.Status == TaskStatus.RanToCompletion) { exceptionAggregator.Execute(dbTask.Result.Dispose); } // there is no else, the db is probably faulted }); ResourcesStoresCache.Clear(); } }); exceptionAggregator.Execute(currentConfiguration.Dispose); exceptionAggregator.Execute(currentDatabase.Dispose); exceptionAggregator.Execute(currentTenantId.Dispose); exceptionAggregator.Execute(bufferPool.Dispose); exceptionAggregator.ThrowIfNeeded(); } finally { disposerLock.ExitWriteLock(); } }