hydrus icon indicating copy to clipboard operation
hydrus copied to clipboard

Schedule database requests according to priority to reduce apparent lag

Open bbappserver opened this issue 4 years ago • 1 comments

Right now hydrus just seems to issue database requests as soon as they are asked for by whatever part of the program wants them (FCFS). This results in the undesirable effect of hydrus seeming unresponsive sometimes, even if the GUI events are still being processed. For example if hydrus begins downloading several PTR updates then requests to the API to get URL info from the companion will take an exceedingly long amount of time.

Instead when a part of hydrus makes a request it should enter a series of queues which are emptied one after another. When making the request the caller should attach a priority.

  • The Immediate Queue (FIFO): If the user alters tags,archive or trash status or issues a search, autocomplete requests, parent resolution, those requests go in this queue. If this queue has any elements they will always be processed first.
  • The High Priority Stack (FILO): If an API (possibly user configurable by token) asks for some information in a way that is interactive (e.g. Get url status) it goes in this queue. This is a stack so if for example the focus changes in the browser causing hydrus companion to issue a new request for the now foreground tab it will be processed first bumping the older requests.
  • The Medium Priority Queue (FIFO) any semi-interactive request, such as subscriptions, PTR processing, building caches.
  • The Low Priority Queue: Background maintenance tasks.

Now the main loop is

while true:
  qt.process_event_queue()
  main_controller.database_requests_controller.process()
  #any other stuff in the main loop
class DatabaseController:
  #methods for enqueueing requests
  def request(priority=0,job):
   if priority ==1: 
      self._tasks_queues[1].insert(0,job) #this list is a stack
   else:
      i=min(i,len(_tasks_queues) #don't overrun the list of queues with bad priority
      self._tasks_queues[i].append(job) #other lists are queue
    
  def process(self):
    n=len(self._tasks_queues)
    for i in n:
      q= self._task_queues[i] #The ith priority queue where the 0th queue is the immediate queue
      
      work_this_queue=True
      #does a higher priority queue have work?
      for j in range(0,i):
         if len(self._task_queues[j]) != 0:
           work_this_queue=False
           break      
      if not work_this_queue:
         #we are yielding to higher precedence so also exit the outer for, we'll come straight back in after a quick detour to the the main loop
         break 

      #The 0th item is the end of the queue, use push to enqueue, or push to the start to use a python list as a stack.
      process_request(q.pop(0))
         
      #Since the qt event queue and hydrus (except twisted.reactor) are running on the same thread it's probably not a bad idea to process the event queue after each database request until you separate them.
      qt.process_event_queue()

bbappserver avatar Nov 01 '20 14:11 bbappserver

Is there any concern here of task preemption causing conflicting changes? I'm not super familiar with the API, but the first thing I think of when I think realtime concurrent REST API POSTs and such is conflicting updates. Like if you set the tags for a file, according to your proposal, it goes in the FIFO queue, so what happens if two tag updates for the same file come in at roughly the same time, but they're contradictory? Like if a user sends a request to delete x from a file, but then they change their mind and want to put it back, well the way that FIFO would work would mean that unless that deletion of x already was completed, then what would happen is you'd evaluate the add operation first, which I think would be considered gibberish to the client since the tag is already there, then you'd evaluate the delete operation as you pop things off the queue. So the end result is x is gone even though you didn't want that. I think you need to do FILO for any queue that can take in operations that could logically contradict each other to prevent jank, but, maybe there's something I'm unaware of that makes my concerns moot.

roachcord3 avatar Jul 22 '23 22:07 roachcord3