cacheDirectoryLimitSvc.py (5135B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\client\script\util\cacheDirectoryLimitSvc.py 2 import datetime 3 import os 4 import stat 5 import blue 6 import telemetry 7 import threading 8 import uthread 9 from carbon.common.script.sys.service import Service 10 from carbon.common.script.sys.serviceConst import ROLE_ANY 11 from watchdog.events import FileSystemEventHandler 12 from watchdog.observers import Observer 13 14 class AutoPrunedDirectory(object): 15 16 def __init__(self, directory, maxDirectorySizeInMB, maxAgeInDays = None): 17 self.directory = directory 18 self.maxDirectorySizeInMB = maxDirectorySizeInMB 19 self.maxAgeInDays = maxAgeInDays 20 self.currentSize = 0 21 self.lastChecked = None 22 23 24 @telemetry.ZONE_FUNCTION 25 def ProcessCacheDirectory(cacheSvc, rootPath, d): 26 filesInDirectory = [] 27 now = datetime.datetime.now() 28 five_minutes = datetime.timedelta(minutes=5) 29 max_age = None if d.maxAgeInDays is None else datetime.timedelta(days=d.maxAgeInDays) 30 if d.lastChecked is not None: 31 if now - d.lastChecked < five_minutes: 32 return 33 d.lastChecked = now 34 d.currentSize = 0 35 for r, _, files in os.walk(rootPath): 36 for f in files: 37 fullpath = os.path.join(r, f) 38 fileSize = os.stat(fullpath)[stat.ST_SIZE] 39 lastAccessed = datetime.datetime.fromtimestamp(os.path.getmtime(fullpath)) 40 if max_age is not None and now - lastAccessed > max_age: 41 cacheSvc.LogInfo('Removing %s from cache. It was last accessed: %s' % (fullpath, lastAccessed)) 42 try: 43 os.remove(fullpath) 44 except OSError: 45 continue 46 47 else: 48 d.currentSize += fileSize 49 filesInDirectory.append((lastAccessed, fileSize, fullpath)) 50 51 currentSize = d.currentSize 52 maxSize = d.maxDirectorySizeInMB * 1024 * 1024 53 if currentSize > maxSize: 54 cacheSvc.LogNotice('Cache Directory %s is over the allocated file size (%sMB, currently %sMB)' % (d.directory, d.maxDirectorySizeInMB, currentSize / 1048576.0)) 55 filesInDirectory.sort(reverse=True) 56 while currentSize > maxSize: 57 fileToDelete = filesInDirectory.pop() 58 if now - fileToDelete[0] < five_minutes: 59 break 60 cacheSvc.LogInfo('Removing %s from cache. It was last accessed: %s' % (fileToDelete[2], fileToDelete[0])) 61 try: 62 os.remove(fileToDelete[2]) 63 currentSize -= fileToDelete[1] 64 except OSError: 65 continue 66 67 else: 68 cacheSvc.LogInfo('Cache Directory %s is currently %sMB (max %sMB)' % (d.directory, currentSize / 1048576.0, d.maxDirectorySizeInMB)) 69 70 71 class FileHandler(FileSystemEventHandler): 72 73 def __init__(self, cacheSvc, rootPath, dirInfo): 74 self.cacheSvc = cacheSvc 75 self.rootPath = rootPath 76 self.dirInfo = dirInfo 77 78 def _ProcessDirectoryInThreadClosure(self): 79 ProcessCacheDirectory(self.cacheSvc, self.rootPath, self.dirInfo) 80 81 def on_any_event(self, event): 82 now = datetime.datetime.now() 83 five_minutes = datetime.timedelta(minutes=5) 84 if self.dirInfo.lastChecked is not None: 85 if now - self.dirInfo.lastChecked < five_minutes: 86 return 87 thread = threading.Thread(target=self._ProcessDirectoryInThreadClosure) 88 thread.start() 89 90 91 class CacheDirectoryLimitService(Service): 92 __guid__ = 'svc.cacheDirectoryLimit' 93 __exportedcalls__ = {'PruneCache': {'role': ROLE_ANY}, 94 'RegisterCacheDirectory': {'role': ROLE_ANY}} 95 96 def __init__(self): 97 super(CacheDirectoryLimitService, self).__init__() 98 self.autoPruneDirectories = [] 99 self._observers = [] 100 101 def Run(self, *etc): 102 Service.Run(self, *etc) 103 self.RegisterCacheDirectory('Avatars', 1024, 5) 104 self.PruneCache() 105 106 def PruneCache(self): 107 uthread.new(self._PruneCache_t) 108 109 def _PruneCache_t(self): 110 cacheDir = blue.paths.ResolvePath(u'cache:/') 111 for d in self.autoPruneDirectories: 112 rootPath = os.path.join(cacheDir, d.directory) 113 uthread.CallOnThread(ProcessCacheDirectory, args=(self, rootPath, d)) 114 115 def RegisterCacheDirectory(self, directory, maxSize, maxLastAccessedAgeInDays = None): 116 for d in self.autoPruneDirectories: 117 if d.directory == directory: 118 d.maxDirectorySizeInMB = maxSize 119 d.maxAgeInDays = maxLastAccessedAgeInDays 120 return 121 122 cacheDir = blue.paths.ResolvePath(u'cache:/') 123 rootPath = os.path.join(cacheDir, directory) 124 d = AutoPrunedDirectory(directory, maxSize, maxLastAccessedAgeInDays) 125 self.autoPruneDirectories.append(d) 126 self.LogNotice('Setting up cache directory watch on %s' % rootPath) 127 observer = Observer() 128 try: 129 observer.schedule(FileHandler(self, rootPath, d), rootPath, recursive=True) 130 observer.start() 131 except OSError: 132 return 133 134 self._observers.append(observer)