Skip to content

_cas: avoid blocking calls to long grpc methods - #2160

Open
abderrahim wants to merge 1 commit into
masterfrom
abderrahim/pull-push-hang
Open

_cas: avoid blocking calls to long grpc methods#2160
abderrahim wants to merge 1 commit into
masterfrom
abderrahim/pull-push-hang

Conversation

@abderrahim

@abderrahim abderrahim commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Long-running synchronous grpc calls block the thread where they are
called, which can cause the scheduler to be unable to terminate the
job.

This commit replaces them with usage of the "asynchronous" future
API. The result() method of a grpc future will not block the thread
while it's waiting, and thus allows terminating the job without
waiting for the blocking call to return. This covers the LocalCAS
methods {Fetch,Upload}{Tree,MissingBlobs}.

As an added benefit, we try to cancel the grpc calls when
terminated. This depends on the server side (buildbox-casd) promptly
cancelling the remote calls, which it doesn't consistently do at the
moment. But that can be fixed independently.

Fixes #2157

Comment thread src/buildstream/_cas/casremote.py
Comment thread src/buildstream/_cas/casremote.py Outdated
Comment on lines +100 to +101
while batch_response_future.running():
time.sleep(1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use batch_response_future.result(timeout=1) to avoid an extra delay of up to 1 second once the call is done?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pushed a change to use this. I can't say I find it more elegant. Feel free to suggest improvements.

(I'm wondering whether it makes sense to have this loop as a utility function)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated version may not be more elegant but it should be slightly more efficient.

I think a helper function would make sense. If we don't need it beyond these two gRPC calls, I wouldn't consider it essential but if it's straight forward, why not?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the gRPC Python code, we might not even need a timeout. The internal wait function seems to make sure that the thread is not blocked for longer than 0.1s. See https://github.com/grpc/grpc/blob/f167d6a879e5b3223c99170663b2cc3e791ac6ad/src/python/grpcio/grpc/_common.py#L119

I haven't verified this but we may be able to call .result() without a loop or timeout.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also do the same to the FetchTree calls in cascache.py, but these ones looked like the more urgent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't verified this but we may be able to call .result() without a loop or timeout.

Seems to be the case indeed. I like this much better :-)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also do the same to the FetchTree calls in cascache.py

Yes, I think we should apply this change also to FetchTree in fetch_directory() and FindMissingBlobs in missing_blobs(). This should cover all gRPC calls involving ordinary remotes (i.e., everything except storage-service). To also cover storage-service, we might have to apply it to practically all gRPC calls.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to cover all instances of {Fetch,Upload}{Tree,MissingBlobs}

@abderrahim
abderrahim force-pushed the abderrahim/pull-push-hang branch 3 times, most recently from c5f3504 to fbe5aeb Compare August 4, 2026 15:13
@abderrahim abderrahim changed the title casremote: avoid blocking calls to {Fetch,Upload}MissingBlobs _cas: avoid blocking calls to long grpc methods Aug 4, 2026
Long-running synchronous grpc calls block the thread where they are
called, which can cause the scheduler to be unable to terminate the
job.

This commit replaces them with usage of the "asynchronous" future
API. The result() method of a grpc future will not block the thread
while it's waiting, and thus allows terminating the job without
waiting for the blocking call to return. This covers the LocalCAS
methods {Fetch,Upload}{Tree,MissingBlobs}.

As an added benefit, we try to cancel the grpc calls when
terminated. This depends on the server side (buildbox-casd) promptly
cancelling the remote calls, which it doesn't consistently do at the
moment. But that can be fixed independently.

Fixes #2157
@abderrahim
abderrahim force-pushed the abderrahim/pull-push-hang branch from fbe5aeb to b1b8fa0 Compare August 4, 2026 15:15
@abderrahim
abderrahim marked this pull request as ready for review August 4, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can't stop artifact pulls sometimes

2 participants