Reputation: 355
Is there a simple pattern to follow to cancel all work and all downstream gRPC requests when an incoming request to a synchronous Python gRPC server is cancelled?
I assumed this was handled, but this page suggests it is not: https://github.com/grpc/grpc/tree/master/examples/python/cancellation
It's important to remember that a gRPC Python server is backed by a thread pool with a fixed size. When an RPC is cancelled, the library does not terminate your servicer thread. It is your responsibility as the application author to ensure that your servicer thread terminates soon after the RPC has been cancelled.
There they handle it by passing down a threading.Event()
which seems cumbersome for a deep call stack. Are there alternatives?
Upvotes: 1
Views: 1477
Reputation: 387
The answer above is indeed correct, but there is still a pattern that allows you to gently and manageably terminate threads.
To transmit to the thread of information about completion, there is a programming pattern - Cancellation Token. It is popular in other programming languages, such as C# and Go (but there it is called differently). Until now, there was no sane implementation of this pattern in Python, but now I have written it.
Install the library cantok
:
pip install cantok
And use like the code in the example:
from random import randint
from threading import Thread
from cantok import ConditionToken, CounterToken, TimeoutToken
counter = 0
def function(token):
global counter
while not token.cancelled:
counter += 1
token = ConditionToken(lambda: randint(1, 100_000) == 1984) + CounterToken(400_000, direct=False) + TimeoutToken(1)
thread = Thread(target=function, args=(token, ))
thread.start()
thread.join()
print(counter)
I'll briefly explain what happens in this code example. We run a function in a separate thread, to which we pass the cancellation token. The function increments the counter until the cancelled
attribute of the cancellation token becomes True
. And this attribute becomes True
when one of the following events occurs: the token was canceled, the time limit of 1 second ended, an unlikely event happened (a random number with a probability of 1 in a billion equals 1984) or the cycle passed 400 thousand iterations. As soon as this happens, we exit the loop and print the counter value.
As you can see, this pattern allows you to easily manage a complex set of restrictions on the duration of the code, as well as easily cancel the operation if desired. Read more about the library's features in the documentation.
Upvotes: 0
Reputation: 1620
I'm the author of the document you're referencing.
I'm afraid there really aren't any alternatives to the application code manually instrumenting a mechanism to stop the servicer thread. This is because there is no mechanism (in Posix or Windows) to "kill a thread."
If it's the actual plumbing of the threading.Event
that you're worried about rather than the Event itself, you could use contextvars
to access the Event
in a thread-safe way that looks roughly like the usage of a global variable.
Upvotes: 2