public ServerFilesystemInfo(ServerFilesystemInfo copy) { _filesystem = copy.Filesystem; _online = copy.Online; _freeBytes = copy.FreeBytes; _totalBytes = copy.TotalBytes; }
/// <summary> /// Gets the first filesystem in lower tier for storage purpose. /// </summary> /// <param name="filesystem">The current filesystem</param> /// <returns></returns> public ServerFilesystemInfo GetLowerTierFilesystemForStorage(ServerFilesystemInfo filesystem) { lock (SyncLock) { List <FilesystemTierEnum> lowerTiers = FindLowerTierFilesystems(filesystem); if (lowerTiers == null || lowerTiers.Count == 0) { return(null); } List <ServerFilesystemInfo> list = new List <ServerFilesystemInfo>(); foreach (FilesystemTierEnum tier in lowerTiers) { list.AddRange(_tierInfo[tier]); } CollectionUtils.Remove(list, fs => !fs.Writeable); list = CollectionUtils.Sort(list, FilesystemSorter.SortByFreeSpace); ServerFilesystemInfo lowtierFilesystem = CollectionUtils.FirstElement(list); if (lowtierFilesystem == null) { return(null); } return(new ServerFilesystemInfo(lowtierFilesystem));//return a copy } }
/// <summary> /// Checks for a storage location for the study in the database, and creates a new location /// in the database if it doesn't exist. /// </summary> /// <param name="partition">The partition where the study is being sent to</param> /// <param name="created"></param> /// <param name="studyDate"></param> /// <param name="studyInstanceUid"></param> /// <param name="syntax"></param> /// <param name="updateContext">The update context to create the study on</param> /// <returns>A <see cref="StudyStorageLocation"/> instance.</returns> public StudyStorageLocation GetOrCreateWritableStudyStorageLocation(string studyInstanceUid, string studyDate, TransferSyntax syntax, IUpdateContext updateContext, ServerPartition partition, out bool created) { created = false; StudyStorageLocation location; try { GetWritableStudyStorageLocation(partition.Key, studyInstanceUid, StudyRestore.True, StudyCache.True, out location); return(location); } catch (StudyNotFoundException) { } FilesystemSelector selector = new FilesystemSelector(Instance); ServerFilesystemInfo filesystem = selector.SelectFilesystem(); if (filesystem == null) { throw new NoWritableFilesystemException(); } IInsertStudyStorage locInsert = updateContext.GetBroker <IInsertStudyStorage>(); InsertStudyStorageParameters insertParms = new InsertStudyStorageParameters { ServerPartitionKey = partition.GetKey(), StudyInstanceUid = studyInstanceUid, Folder = ResolveStorageFolder(partition.Key, studyInstanceUid, studyDate, updateContext, false /* set to false for optimization because we are sure it's not in the system */), FilesystemKey = filesystem.Filesystem.GetKey(), QueueStudyStateEnum = QueueStudyStateEnum.Idle }; if (syntax.LosslessCompressed) { insertParms.TransferSyntaxUid = syntax.UidString; insertParms.StudyStatusEnum = StudyStatusEnum.OnlineLossless; } else if (syntax.LossyCompressed) { insertParms.TransferSyntaxUid = syntax.UidString; insertParms.StudyStatusEnum = StudyStatusEnum.OnlineLossy; } else { insertParms.TransferSyntaxUid = syntax.UidString; insertParms.StudyStatusEnum = StudyStatusEnum.Online; } location = locInsert.FindOne(insertParms); created = true; return(location); }
/// <summary> /// Find lower tier filesystems. /// </summary> /// <param name="filesystem"></param> /// <returns></returns> private List <FilesystemTierEnum> FindLowerTierFilesystems(ServerFilesystemInfo filesystem) { List <FilesystemTierEnum> lowerTiers = new List <FilesystemTierEnum>(); foreach (FilesystemTierEnum tier in _tierInfo.Keys) { if (tier.Enum > filesystem.Filesystem.FilesystemTierEnum.Enum) { lowerTiers.Add(tier); } } return(lowerTiers); }
public static int SortByFreeSpace(ServerFilesystemInfo fs1, ServerFilesystemInfo fs2) { Platform.CheckForNullReference(fs1, "fs1"); Platform.CheckForNullReference(fs2, "fs2"); Platform.CheckForNullReference(fs1.Filesystem, "fs1.Filesystem"); Platform.CheckForNullReference(fs2.Filesystem, "fs2.Filesystem"); if (fs1 == fs2) return 0; if (fs1.Filesystem.FilesystemTierEnum.Enum.Equals(fs2.Filesystem.FilesystemTierEnum.Enum)) { // descending order on available size.. smaller margin means less available space return fs2.HighwaterMarkMargin.CompareTo(fs1.HighwaterMarkMargin); } else { // ascending order on tier return fs1.Filesystem.FilesystemTierEnum.Enum.CompareTo(fs2.Filesystem.FilesystemTierEnum.Enum); } }
/// <summary> /// Helper method to verify if the specified <see cref="StudyStorageLocation"/> /// is writable and throw <see cref="FilesystemNotWritableException"/> if it is not. /// </summary> /// <param name="location"></param> public void EnsureStorageLocationIsWritable(StudyStorageLocation location) { ServerFilesystemInfo fs = GetFilesystemInfo(location.FilesystemKey); if (!fs.Enable) { throw new FilesystemNotWritableException(fs.Filesystem.FilesystemPath) { Reason = "It is disabled" } } ; if (!fs.Online) { throw new FilesystemNotWritableException(fs.Filesystem.FilesystemPath) { Reason = "It is offline/unreachable" } } ; if (fs.ReadOnly) { throw new FilesystemNotWritableException(fs.Filesystem.FilesystemPath) { Reason = "It is read-only" } } ; if (fs.Full) { throw new FilesystemNotWritableException(fs.Filesystem.FilesystemPath) { Reason = "It is full" } } ; }
public static int SortByFreeSpace(ServerFilesystemInfo fs1, ServerFilesystemInfo fs2) { Platform.CheckForNullReference(fs1, "fs1"); Platform.CheckForNullReference(fs2, "fs2"); Platform.CheckForNullReference(fs1.Filesystem, "fs1.Filesystem"); Platform.CheckForNullReference(fs2.Filesystem, "fs2.Filesystem"); if (fs1 == fs2) { return(0); } if (fs1.Filesystem.FilesystemTierEnum.Enum.Equals(fs2.Filesystem.FilesystemTierEnum.Enum)) { // descending order on available size.. smaller margin means less available space return(fs2.HighwaterMarkMargin.CompareTo(fs1.HighwaterMarkMargin)); } else { // ascending order on tier return(fs1.Filesystem.FilesystemTierEnum.Enum.CompareTo(fs2.Filesystem.FilesystemTierEnum.Enum)); } }
public ServerFilesystemInfo SelectFilesystem() { IList <ServerFilesystemInfo> list = new List <ServerFilesystemInfo>(_monitor.GetFilesystems()); IList <ServerFilesystemInfo> writableFS = CollectionUtils.Select(list, delegate(ServerFilesystemInfo fs) { return(fs.Writeable); }); StringBuilder log = new StringBuilder(); if (writableFS == null || writableFS.Count == 0) { log.AppendLine("No writable storage found"); foreach (ServerFilesystemInfo fs in list) { log.AppendLine(string.Format("\t{0} : {1}", fs.Filesystem.Description, fs.StatusString)); } Platform.Log(LogLevel.Warn, log.ToString()); return(null); } writableFS = CollectionUtils.Sort(writableFS, FilesystemSorter.SortByFreeSpace); ServerFilesystemInfo selectedFS = CollectionUtils.FirstElement(writableFS); Platform.CheckForNullReference(selectedFS, "selectedFS"); return(selectedFS); }
/// <summary> /// Load filesystem information from the database. /// </summary> private void LoadFilesystems() { bool changed = false; lock (SyncLock) { try { List <FilesystemTierEnum> tiers = FilesystemTierEnum.GetAll(); // sorted by enum values tiers.Sort((tier1, tier2) => tier1.Enum.CompareTo(tier2.Enum)); _tierInfo = new TierInfo(); foreach (FilesystemTierEnum tier in tiers) { _tierInfo.Add(tier, new List <ServerFilesystemInfo>()); } using (IReadContext read = _store.OpenReadContext()) { IFilesystemEntityBroker filesystemSelect = read.GetBroker <IFilesystemEntityBroker>(); FilesystemSelectCriteria criteria = new FilesystemSelectCriteria(); IList <Filesystem> filesystemList = filesystemSelect.Find(criteria); foreach (Filesystem filesystem in filesystemList) { if (_filesystemList.ContainsKey(filesystem.Key)) { if ((filesystem.HighWatermark != _filesystemList[filesystem.Key].Filesystem.HighWatermark) || (filesystem.LowWatermark != _filesystemList[filesystem.Key].Filesystem.LowWatermark)) { Platform.Log(LogLevel.Info, "Watermarks have changed for filesystem {0}, Low: {1}, High: {2}", filesystem.Description, filesystem.LowWatermark, filesystem.HighWatermark); } _filesystemList[filesystem.Key].Filesystem = filesystem; _tierInfo[filesystem.FilesystemTierEnum].Add(_filesystemList[filesystem.Key]); } else { ServerFilesystemInfo info = new ServerFilesystemInfo(filesystem); _filesystemList.Add(filesystem.Key, info); _tierInfo[filesystem.FilesystemTierEnum].Add(info); info.LoadFreeSpace(); changed = true; } } } if (changed && _changedListener != null) { EventsHelper.Fire(_changedListener, this, new FilesystemChangedEventArgs(this)); } } catch (Exception ex) { Platform.Log(LogLevel.Error, ex, "Exception has occurred while updating the filesystem list from the datbase. Retry later"); } } }
/// <summary> /// Returns a value indicating whether the filesystem with the specified key /// is writable. /// </summary> /// <param name="key"></param> /// <returns></returns> public bool IsWritable(ServerEntityKey key) { ServerFilesystemInfo fs = GetFilesystemInfo(key); return(fs.Writeable); }
private void PurgeStudies(Model.ServiceLock item, ServerFilesystemInfo fs) { Platform.Log(LogLevel.Info, "Starting query for Filesystem Purge candidates on '{0}'.", fs.Filesystem.Description); try { DoStudyPurge(fs, item); } catch (Exception e) { Platform.Log(LogLevel.Error, e, "Unexpected exception when processing StudyDelete records."); } Platform.Log(LogLevel.Info, "{0} studies have been scheduled for purging from filesystem '{1}'", _studiesPurged, fs.Filesystem.Description); }
private void MigrateStudies(Model.ServiceLock item, ServerFilesystemInfo fs) { ServerFilesystemInfo newFilesystem = FilesystemMonitor.Instance.GetLowerTierFilesystemForStorage(fs); if (newFilesystem == null) { Platform.Log(LogLevel.Warn, "No writable storage in lower tiers. Tier-migration for '{0}' is disabled at this time.", fs.Filesystem.Description); return; } Platform.Log(LogLevel.Info, "Starting Tier Migration from {0}", fs.Filesystem.Description); try { DoStudyMigrate(fs, item); } catch (Exception e) { Platform.Log(LogLevel.Error, e, "Unexpected exception when scheduling tier-migration."); } Platform.Log(LogLevel.Info, "{0} studies have been scheduled for migration from filesystem '{1}'", _studiesMigrated, fs.Filesystem.Description); }
/// <summary> /// Do the actual Study migration. /// </summary> /// <param name="fs">The filesystem</param> /// <param name="item">The ServiceLock item being processed.</param> private void DoStudyMigrate( ServerFilesystemInfo fs, Model.ServiceLock item) { FilesystemQueueTypeEnum type = FilesystemQueueTypeEnum.TierMigrate; while (_bytesToRemove > 0) { TimeSpanStatistics queryTime = new TimeSpanStatistics("Query Time"); queryTime.Start(); IList<FilesystemQueue> list = GetFilesystemQueueCandidates(item, Platform.Time, type, false); queryTime.End(); Platform.Log(LogLevel.Info, "{1:0.0} MBs needs to be removed from '{0}'. Found {2} studies that can be migrated in {3} seconds", fs.Filesystem.Description, _bytesToRemove / (1024 * 1024), list.Count, queryTime.Value.TotalSeconds); if (list.Count > 0) { ProcessStudyMigrateCandidates(list); if (CancelPending) break; } else { break; } } }
/// <summary> /// Do the actual StudyPurge /// </summary> /// <param name="item"></param> /// <param name="fs">The filesystem being worked on.</param> private void DoStudyPurge(ServerFilesystemInfo fs, Model.ServiceLock item) { DateTime deleteTime = Platform.Time; FilesystemQueueTypeEnum type = FilesystemQueueTypeEnum.PurgeStudy; while (_bytesToRemove > 0) { Platform.Log(LogLevel.Debug, "{1:0.0} MBs needs to be removed from '{0}'. Querying for studies that can be purged", fs.Filesystem.Description, _bytesToRemove / (1024 * 1024)); IList<FilesystemQueue> list = GetFilesystemQueueCandidates(item, deleteTime, type, false); if (list.Count > 0) { ProcessStudyPurgeCandidates(list); if (CancelPending) break; } else { // No candidates break; } } }
/// <summary> /// Updates the 'State' of the filesystem associated with the 'FilesystemDelete' <see cref="ServiceLock"/> item /// </summary> /// <param name="item"></param> /// <param name="fs"></param> private static void UpdateState(Model.ServiceLock item, ServerFilesystemInfo fs) { FilesystemState state = null; if (item.State != null && item.State.DocumentElement!=null) { //load from datatabase state = XmlUtils.Deserialize<FilesystemState>(item.State.DocumentElement); } if (state == null) state = new FilesystemState(); if (fs.AboveHighWatermark) { // we don't want to generate alert if the filesystem is offline or not accessible. if (fs.Online && (fs.Readable || fs.Writeable)) { TimeSpan ALERT_INTERVAL = TimeSpan.FromMinutes(ServiceLockSettings.Default.HighWatermarkAlertInterval); if (state.AboveHighWatermarkTimestamp == null) state.AboveHighWatermarkTimestamp = Platform.Time; TimeSpan elapse = (state.LastHighWatermarkAlertTimestamp != null) ? Platform.Time - state.LastHighWatermarkAlertTimestamp.Value : Platform.Time - state.AboveHighWatermarkTimestamp.Value; if (elapse.Duration() >= ALERT_INTERVAL) { ServerPlatform.Alert(AlertCategory.System, AlertLevel.Warning, "Filesystem", AlertTypeCodes.LowResources, null, TimeSpan.Zero, SR.AlertFilesystemAboveHW, fs.Filesystem.Description, TimeSpanFormatter.Format(Platform.Time - state.AboveHighWatermarkTimestamp.Value, true)); state.LastHighWatermarkAlertTimestamp = Platform.Time; } } else { state.AboveHighWatermarkTimestamp = null; state.LastHighWatermarkAlertTimestamp = null; } } else { state.AboveHighWatermarkTimestamp = null; state.LastHighWatermarkAlertTimestamp = null; } XmlDocument stateXml = new XmlDocument(); stateXml.AppendChild(stateXml.ImportNode(XmlUtils.Serialize(state), true)); IPersistentStore store = PersistentStoreRegistry.GetDefaultStore(); using (IUpdateContext ctx = store.OpenUpdateContext(UpdateContextSyncMode.Flush)) { ServiceLockUpdateColumns columns = new ServiceLockUpdateColumns(); columns.State = stateXml; IServiceLockEntityBroker broker = ctx.GetBroker<IServiceLockEntityBroker>(); broker.Update(item.GetKey(), columns); ctx.Commit(); } }
private bool ArchiveLogs(ServerFilesystemInfo archiveFs) { string archivePath = Path.Combine(archiveFs.Filesystem.FilesystemPath, "AlertLog"); DateTime cutOffTime = Platform.Time.Date.AddDays(ServiceLockSettings.Default.AlertCachedDays*-1); AlertSelectCriteria criteria = new AlertSelectCriteria(); criteria.InsertTime.LessThan(cutOffTime); criteria.InsertTime.SortAsc(0); using (ServerExecutionContext context = new ServerExecutionContext()) { IAlertEntityBroker broker = context.ReadContext.GetBroker<IAlertEntityBroker>(); ImageServerLogWriter<Alert> writer = new ImageServerLogWriter<Alert>(archivePath, "Alert"); List<ServerEntityKey> keyList = new List<ServerEntityKey>(500); try { broker.Find(criteria, delegate(Alert result) { keyList.Add(result.Key); // If configured, don't flush to disk. We just delete the contents of keyList below. if (!ServiceLockSettings.Default.AlertDelete) { if (writer.WriteLog(result, result.InsertTime)) { // The logs been flushed, delete the log entries cached. using ( IUpdateContext update = PersistentStoreRegistry.GetDefaultStore().OpenUpdateContext(UpdateContextSyncMode.Flush) ) { IApplicationLogEntityBroker updateBroker = update.GetBroker<IApplicationLogEntityBroker>(); foreach (ServerEntityKey key in keyList) updateBroker.Delete(key); update.Commit(); } keyList = new List<ServerEntityKey>(); } } }); writer.FlushLog(); if (keyList.Count > 0) { using ( IUpdateContext update = PersistentStoreRegistry.GetDefaultStore().OpenUpdateContext(UpdateContextSyncMode.Flush)) { IAlertEntityBroker updateBroker = update.GetBroker<IAlertEntityBroker>(); foreach (ServerEntityKey key in keyList) updateBroker.Delete(key); update.Commit(); } } } catch (Exception e) { Platform.Log(LogLevel.Error, e, "Unexpected exception when purging Alert log files."); writer.Dispose(); return false; } writer.Dispose(); return true; } }
private bool ArchiveLogs(ServerFilesystemInfo archiveFs) { try { using (ServerExecutionContext context = new ServerExecutionContext()) { string archivePath = Path.Combine(archiveFs.Filesystem.FilesystemPath, "ApplicationLog"); ApplicationLogSelectCriteria criteria = new ApplicationLogSelectCriteria(); criteria.Timestamp.SortAsc(0); IApplicationLogEntityBroker broker = context.ReadContext.GetBroker<IApplicationLogEntityBroker>(); ApplicationLog firstLog = broker.FindOne(criteria); if (firstLog == null) return true; DateTime currentCutOffTime = firstLog.Timestamp.AddMinutes(5); int cachedDays = ServiceLockSettings.Default.ApplicationLogCachedDays; if (cachedDays < 0) cachedDays = 0; DateTime cutOffTime = Platform.Time.Date.AddDays(cachedDays*-1); if (currentCutOffTime > cutOffTime) return true; using ( ImageServerLogWriter<ApplicationLog> writer = new ImageServerLogWriter<ApplicationLog>(archivePath, "ApplicationLog")) { while (currentCutOffTime < cutOffTime) { if (!ArchiveTimeRange(writer, currentCutOffTime)) { writer.FlushLog(); return false; } currentCutOffTime = currentCutOffTime.AddMinutes(5); } // Now flush the last potential 5 minutes. if (!ArchiveTimeRange(writer, cutOffTime)) { writer.FlushLog(); return false; } writer.FlushLog(); } return true; } } catch (Exception e) { Platform.Log(LogLevel.Error, e, "Unexpected exception when writing log file."); return false; } }