2021-08-16 20:15:12 +02:00
|
|
|
import asyncio
|
|
|
|
import logging
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
2021-08-06 16:44:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
class DiskSpaceManager:
|
|
|
|
|
2021-10-01 03:38:02 +02:00
|
|
|
def __init__(self, config, db, blob_manager, cleaning_interval=30 * 60, analytics=None):
|
2021-08-06 16:44:57 +02:00
|
|
|
self.config = config
|
2021-09-08 16:55:21 +02:00
|
|
|
self.db = db
|
|
|
|
self.blob_manager = blob_manager
|
2021-08-16 20:15:12 +02:00
|
|
|
self.cleaning_interval = cleaning_interval
|
|
|
|
self.running = False
|
|
|
|
self.task = None
|
2021-10-01 03:38:02 +02:00
|
|
|
self.analytics = analytics
|
2021-11-03 19:38:32 +01:00
|
|
|
self._used_space_bytes = None
|
2021-08-06 16:44:57 +02:00
|
|
|
|
2021-10-25 21:44:19 +02:00
|
|
|
async def get_free_space_mb(self, is_network_blob=False):
|
2021-10-24 23:19:06 +02:00
|
|
|
limit_mb = self.config.network_storage_limit if is_network_blob else self.config.blob_storage_limit
|
2021-11-03 02:34:15 +01:00
|
|
|
space_used_mb = await self.get_space_used_mb()
|
|
|
|
space_used_mb = space_used_mb['network_storage'] if is_network_blob else space_used_mb['content_storage']
|
|
|
|
return max(0, limit_mb - space_used_mb)
|
2021-10-24 23:19:06 +02:00
|
|
|
|
2021-11-03 02:34:15 +01:00
|
|
|
async def get_space_used_bytes(self):
|
2021-11-03 19:38:32 +01:00
|
|
|
self._used_space_bytes = await self.db.get_stored_blob_disk_usage()
|
|
|
|
return self._used_space_bytes
|
2021-08-06 16:44:57 +02:00
|
|
|
|
2021-11-03 19:38:32 +01:00
|
|
|
async def get_space_used_mb(self, cached=True):
|
|
|
|
cached = cached and self._used_space_bytes is not None
|
|
|
|
space_used_bytes = self._used_space_bytes if cached else await self.get_space_used_bytes()
|
2021-11-03 02:34:15 +01:00
|
|
|
return {key: int(value/1024.0/1024.0) for key, value in space_used_bytes.items()}
|
2021-08-16 20:15:12 +02:00
|
|
|
|
2021-09-08 16:55:21 +02:00
|
|
|
async def clean(self):
|
2021-10-22 07:43:40 +02:00
|
|
|
await self._clean(False)
|
|
|
|
await self._clean(True)
|
|
|
|
|
2021-10-24 23:19:06 +02:00
|
|
|
async def _clean(self, is_network_blob=False):
|
2021-11-03 02:34:15 +01:00
|
|
|
space_used_bytes = await self.get_space_used_bytes()
|
|
|
|
if is_network_blob:
|
|
|
|
space_used_bytes = space_used_bytes['network_storage']
|
|
|
|
else:
|
|
|
|
space_used_bytes = space_used_bytes['content_storage'] + space_used_bytes['private_storage']
|
2021-10-24 23:19:06 +02:00
|
|
|
storage_limit_mb = self.config.network_storage_limit if is_network_blob else self.config.blob_storage_limit
|
|
|
|
storage_limit = storage_limit_mb*1024*1024 if storage_limit_mb else None
|
2021-10-01 03:38:02 +02:00
|
|
|
if self.analytics:
|
2021-10-22 08:00:14 +02:00
|
|
|
asyncio.create_task(
|
2021-10-24 23:19:06 +02:00
|
|
|
self.analytics.send_disk_space_used(space_used_bytes, storage_limit, is_network_blob)
|
2021-10-22 08:00:14 +02:00
|
|
|
)
|
2021-10-01 03:38:02 +02:00
|
|
|
if not storage_limit:
|
2021-08-16 23:03:40 +02:00
|
|
|
return 0
|
2021-09-08 16:55:21 +02:00
|
|
|
delete = []
|
2021-10-01 03:38:02 +02:00
|
|
|
available = storage_limit - space_used_bytes
|
2021-09-10 16:53:52 +02:00
|
|
|
if available > 0:
|
|
|
|
return 0
|
2021-10-24 23:19:06 +02:00
|
|
|
for blob_hash, file_size, _ in await self.db.get_stored_blobs(is_mine=False, is_network_blob=is_network_blob):
|
2021-09-10 16:53:52 +02:00
|
|
|
delete.append(blob_hash)
|
2021-08-16 20:15:12 +02:00
|
|
|
available += file_size
|
|
|
|
if available > 0:
|
|
|
|
break
|
2021-09-08 16:55:21 +02:00
|
|
|
if delete:
|
2021-09-15 16:37:08 +02:00
|
|
|
await self.db.stop_all_files()
|
2021-09-08 16:55:21 +02:00
|
|
|
await self.blob_manager.delete_blobs(delete, delete_from_db=True)
|
|
|
|
return len(delete)
|
2021-08-16 20:15:12 +02:00
|
|
|
|
|
|
|
async def cleaning_loop(self):
|
|
|
|
while self.running:
|
|
|
|
await asyncio.sleep(self.cleaning_interval)
|
2021-09-08 16:55:21 +02:00
|
|
|
await self.clean()
|
2021-08-16 20:15:12 +02:00
|
|
|
|
|
|
|
async def start(self):
|
|
|
|
self.running = True
|
|
|
|
self.task = asyncio.create_task(self.cleaning_loop())
|
|
|
|
self.task.add_done_callback(lambda _: log.info("Stopping blob cleanup service."))
|
|
|
|
|
|
|
|
async def stop(self):
|
|
|
|
if self.running:
|
|
|
|
self.running = False
|
|
|
|
self.task.cancel()
|