Gitaly Client: encapsulate GRPC errors
Problem to solve
Encapsulate all expected GRPC exceptions into GitlayClient own exceptions, and provide extra context and meaningful errors.
Motivation here is to provide Human Errors that provide "Context", "Feedback" and "Recovery": See talk: https://olivierlacan.com/talks/human-errors/
By encapsulating the GRPC errors, we now have something we can rescue on the application side instead of a generic 503 error page.
We can use this new exceptions to degrade gracefully (ex: render standard layout with all menus and headers and a nice message telling user that repository storage is under heavy load, to retry after waiting a few minutes.
Providing this type of information is better than a generic 503. Instead of "hey GitLab is buggy", they will understand "Hey, everybody is using GitLab so they are having scalling problems right now".
Further details
When gitaly_client.rb talks to remote gitaly-go via GRPC, there are many different errors that can happen. It should rescue the ones it is aware of and encapsulates so we can safely rescue and/or provide meaningful errors when needed.
One example:
GRPC::DeadlineExceeded. We expect this to happen, even if we prefer then not to. This happens almost everytime you start GDK with cold cache.
Instead of a big stacktrace without helpful information:
Proposal
Identify all common GRPC exceptions that we expect to happen and encapsulate from gitaly_client and enhance with context metadata.
(see example below)
What does success look like, and how can we measure that?
As an exemple of how we can improve the Exceptions:
Instead of previous GRPC::DeadlineExceeded would be nice to have something like:
Gitaly::TimeoutError - "Gitaly client for storage: '#{ex.storage}' tried to communicate with service: `#{ex.service}`, rpc: `#{ex.rpc}` but it took more than timeout limit: `#{ex.timeout}` to receive a response"
lib/gitlab/gitaly_client.rb:134:in `call'
lib/gitlab/gitaly_client/repository_service.rb:17:in `exists?'
lib/gitlab/git/repository.rb:147:in `exists?'
app/models/repository.rb:506:in `exists?'
...