0

There is a costly API call that takes couple of seconds and writes a lot of data. I try to limit the calls made to once every interval seconds. The problem is, that IF the API gets called, it usually gets called multiple times at once.

So what I did:

class LimitAPICalls:
    def decide_for_poll(self, interval):
        self.time_now = datetime.datetime.now()
        try:
            time_since_last_run = (self.time_now - self.time_old).seconds
        except Exception as E:
            time_since_last_run = interval

        if time_since_last_run >= interval:
            self.time_old = datetime.datetime.now()
            return True
        return False

    def call_wrapper(self):
        if self.decide_for_poll(interval = 30) == True:
            print("actually calling")
            self.result = the_call_itself()
        try:
            print("returning values from memory")
            return self.result
        except AttributeError:
            print("calling because of AttributeError")
            self.time_old = datetime.datetime.now()
            return the_call_itself()

What I see in the console, specially when reinitializing, is more or less always the time a pattern similar to:

actually calling 
returning values from memoryreturning values from memory
returning values from memorycalling because of AttributeError
calling because of AttributeError
calling because of AttributeError 
returning values from memory 
calling because of AttributeError    
returning values from memory returning values from memory
returning values from memory returning values from memoryreturning values from memory
returning values from memory

But what I want is that the first call ensures that for the time of the interval is the only call being made. So the call is being made multipe times, because it is not finished yet.

The calls come from the browser, all of them at once - how do I ensure that only one call is made in the time of interval?

xtlc
  • 1,070
  • 1
  • 15
  • 41
  • You're missing an if-else... You do the call if the interval was long enough, then you do the call again. – Guimoute Apr 10 '22 at 15:30
  • Can you be more precise (sorry you might be 100% right, I just starred at this for some time now). – xtlc Apr 10 '22 at 15:33
  • In `call_wrapper`, `the_call_itself` appears three times. The function is called once if the last time it was called is more than ago, then you call it again regardless of the condition in your try-except block. – Guimoute Apr 10 '22 at 15:34
  • sorry. that was a copy & paste error on my side. You were right. Please look at it again :) The problem is the same as before. – xtlc Apr 10 '22 at 15:38
  • I think the problem is, when 5 calls hit "at once" that the first is not being done, before the seond - fifth start. So I would need a mechanism that the second is waiting for the first to complete. – xtlc Apr 10 '22 at 15:40

1 Answers1

1

Can you try with this? I removed all your try-except blocks because they are not necessary.


class LimitAPICalls:

    def __init__(self): 
        self.time_old = datetime.datetime(1, 1, 1) # A long time ago so that the first comparison is always true.
        self.result = None

    def poll(self):
        return NotImplemented # The costly API request goes here.

    def decide_for_poll(self, interval) -> bool:      
        time_now = datetime.datetime.now()
        time_since_last_run = (time_now - self.time_old).seconds
        if time_since_last_run >= interval:
            self.time_old = datetime.datetime.now()
            return True
        return False

    def call_wrapper(self):
        if self.decide_for_poll(interval=30) == True:
            print("actually calling")
            self.result = self.poll()
        else:
            print("returning values from memory")
        return self.result 

Guimoute
  • 4,407
  • 3
  • 12
  • 28
  • there is a small error in the `time_now` variable, which is obsolete anyways. But this does the trick, thanks! – xtlc Apr 10 '22 at 16:28
  • this sometimes fails btw ... the empty result is returned and the call is not executed. – xtlc Apr 10 '22 at 16:36
  • I guess nobody can help you further without knowing the content of the call and how you run this code. Is it based on `requests`? – Guimoute Apr 10 '22 at 17:13
  • yes it is ... for now I just solved it by checking the output of `self.result` and if the type is `None` I repeat the process. – xtlc Apr 10 '22 at 18:54
  • 1
    Also take a look [here](https://stackoverflow.com/q/16511337/9282844) for more detailed error checks. – Guimoute Apr 10 '22 at 20:07