Search code examples
pythonasynchronouscoroutinegevent

Gevent take first result to complete


I currently have some code which makes requests to a primary primary set of databases. Each request could take more than the timeout. I want to read the result of the one to finish first and decide whether to wait for the second. The code looks something like this:

GREENLET_TIMEOUT = 1  # Seconds
greenlet_a = gevent.spawn(start_request, redis_a)
greenlet_b = gevent.spawn(start_request, redis_b)
gevent.joinall([greenlet_a, greenlet_b], timeout=GREENLET_TIMEOUT)

result_a = greenlet_a.value
result_b = greenlet_b.value

I saw a similar post here I tried to do what it suggested here:

import time
import gevent
from gevent.event import Event

def first():
  time.sleep(1)
  return

def second():
  time.sleep(5)
  return

event = gevent.event.Event()
event.clear()

def callback(value):
    event.set()

lst = [gevent.spawn(first), gevent.spawn(second)]
for g in lst:
    g.link(callback)

start_time = time.time()
res = event.wait()
print("--- %s seconds ---" % (time.time() - start_time))
print(res)

I expect it to take a little over 1 second and return but it is obviously waiting for both to complete.

I also tried another approach using AsyncResult

import time
import gevent
from gevent.event import AsyncResult

result = AsyncResult()

def first():
  result.set(1)

def second():
  time.sleep(5)
  result.set(2)

first_event = gevent.spawn(first)
second_event = gevent.spawn(second)

res = result.get()
print(res)

Any help would be appreciated!


Solution

  • I was missing the monkey patch part. This works as expected!

    import time
    import gevent
    from gevent.event import AsyncResult
    from gevent import monkey
    monkey.patch_all(subprocess=True)
    
    result = AsyncResult()
    
    def first():
      result.set(1)
    
    def second():
      time.sleep(5)
      result.set(2)
    
    first_event = gevent.spawn(first)
    second_event = gevent.spawn(second)
    
    res = result.get()
    print(res)