228 lines
6.9 KiB
Python
228 lines
6.9 KiB
Python
import os
|
|
import threading
|
|
import time
|
|
|
|
from hydrus.core import HydrusData
|
|
from hydrus.core import HydrusExceptions
|
|
from hydrus.core import HydrusGlobals as HG
|
|
from hydrus.core import HydrusThreading
|
|
from hydrus.core import HydrusTime
|
|
|
|
from hydrus.client import ClientGlobals as CG
|
|
from hydrus.client.interfaces import ClientControllerInterface
|
|
|
|
class DatabaseMaintenanceManager( object ):
|
|
|
|
def __init__( self, controller: ClientControllerInterface.ClientControllerInterface ):
|
|
|
|
self._controller = controller
|
|
|
|
self._lock = threading.Lock()
|
|
|
|
self._serious_error_encountered = False
|
|
|
|
self._wake_event = threading.Event()
|
|
self._shutdown = False
|
|
self._is_working_hard = False
|
|
|
|
self._controller.sub( self, 'Shutdown', 'shutdown' )
|
|
self._controller.sub( self, 'Wake', 'checkbox_manager_inverted' )
|
|
self._controller.sub( self, 'Wake', 'notify_deferred_delete_database_maintenance_new_work' )
|
|
|
|
|
|
def _AbleToDoBackgroundMaintenance( self ):
|
|
|
|
if self._is_working_hard:
|
|
|
|
return True
|
|
|
|
|
|
if CG.client_controller.CurrentlyIdle():
|
|
|
|
if not self._controller.new_options.GetBoolean( 'database_deferred_delete_maintenance_during_idle' ):
|
|
|
|
return False
|
|
|
|
|
|
if not self._controller.GoodTimeToStartBackgroundWork():
|
|
|
|
return False
|
|
|
|
|
|
else:
|
|
|
|
if not self._controller.new_options.GetBoolean( 'database_deferred_delete_maintenance_during_active' ):
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
def _GetWaitPeriod( self, work_period: float, time_it_took: float, still_work_to_do: bool ):
|
|
|
|
if not still_work_to_do:
|
|
|
|
return 600
|
|
|
|
|
|
if self._is_working_hard:
|
|
|
|
rest_ratio = CG.client_controller.new_options.GetInteger( 'deferred_table_delete_rest_percentage_work_hard' ) / 100
|
|
|
|
elif CG.client_controller.CurrentlyIdle():
|
|
|
|
rest_ratio = CG.client_controller.new_options.GetInteger( 'deferred_table_delete_rest_percentage_idle' ) / 100
|
|
|
|
else:
|
|
|
|
rest_ratio = CG.client_controller.new_options.GetInteger( 'deferred_table_delete_rest_percentage_normal' ) / 100
|
|
|
|
|
|
reasonable_work_time = min( 5 * work_period, time_it_took )
|
|
|
|
return reasonable_work_time * rest_ratio
|
|
|
|
|
|
def _GetWorkPeriod( self ):
|
|
|
|
if self._is_working_hard:
|
|
|
|
return CG.client_controller.new_options.GetInteger( 'deferred_table_delete_work_time_ms_work_hard' ) / 1000
|
|
|
|
elif CG.client_controller.CurrentlyIdle():
|
|
|
|
return CG.client_controller.new_options.GetInteger( 'deferred_table_delete_work_time_ms_idle' ) / 1000
|
|
|
|
else:
|
|
|
|
return CG.client_controller.new_options.GetInteger( 'deferred_table_delete_work_time_ms_normal' ) / 1000
|
|
|
|
|
|
|
|
def MainLoopBackgroundWork( self ):
|
|
|
|
def check_shutdown():
|
|
|
|
if HydrusThreading.IsThreadShuttingDown() or self._shutdown or self._serious_error_encountered:
|
|
|
|
raise HydrusExceptions.ShutdownException()
|
|
|
|
|
|
|
|
try:
|
|
|
|
time_to_start = HydrusTime.GetNow() + 15
|
|
|
|
while not HydrusTime.TimeHasPassed( time_to_start ):
|
|
|
|
check_shutdown()
|
|
|
|
time.sleep( 1 )
|
|
|
|
|
|
while True:
|
|
|
|
still_work_to_do = False
|
|
|
|
check_shutdown()
|
|
|
|
self._controller.WaitUntilViewFree()
|
|
|
|
with self._lock:
|
|
|
|
able_to_work = self._AbleToDoBackgroundMaintenance()
|
|
work_period = self._GetWorkPeriod()
|
|
|
|
|
|
time_it_took = 1.0
|
|
|
|
if able_to_work:
|
|
|
|
time_to_stop = HydrusTime.GetNowFloat() + work_period
|
|
|
|
start_time = HydrusTime.GetNowFloat()
|
|
|
|
try:
|
|
|
|
still_work_to_do = CG.client_controller.WriteSynchronous( 'do_deferred_table_delete_work', time_to_stop )
|
|
|
|
except Exception as e:
|
|
|
|
self._serious_error_encountered = True
|
|
|
|
HydrusData.PrintException( e )
|
|
|
|
message = 'There was an unexpected problem during deferred table delete database maintenance work! This maintenance system will not run again this boot. A full traceback of this error should be written to the log.'
|
|
message += os.linesep * 2
|
|
message += str( e )
|
|
|
|
HydrusData.ShowText( message )
|
|
|
|
finally:
|
|
|
|
self._controller.pub( 'notify_deferred_delete_database_maintenance_work_complete' )
|
|
|
|
|
|
time_it_took = HydrusTime.GetNowFloat() - start_time
|
|
|
|
|
|
with self._lock:
|
|
|
|
wait_period = self._GetWaitPeriod( work_period, time_it_took, still_work_to_do )
|
|
|
|
|
|
self._wake_event.wait( wait_period )
|
|
|
|
self._wake_event.clear()
|
|
|
|
|
|
except HydrusExceptions.ShutdownException:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def FlipWorkingHard( self ):
|
|
|
|
with self._lock:
|
|
|
|
self._is_working_hard = not self._is_working_hard
|
|
|
|
|
|
self._controller.pub( 'notify_deferred_delete_database_maintenance_state_change' )
|
|
|
|
self.Wake()
|
|
|
|
|
|
def IsWorkingHard( self ) -> bool:
|
|
|
|
with self._lock:
|
|
|
|
return self._is_working_hard
|
|
|
|
|
|
|
|
def Shutdown( self ):
|
|
|
|
with self._lock:
|
|
|
|
self._shutdown = True
|
|
|
|
|
|
self.Wake()
|
|
|
|
|
|
|
|
def Start( self ):
|
|
|
|
self._controller.CallToThreadLongRunning( self.MainLoopBackgroundWork )
|
|
|
|
|
|
def Wake( self ):
|
|
|
|
self._wake_event.set()
|
|
|
|
|