fetchToFile writes oid to the batch process stdin, reads the response header and streams the blob content directly to dstPath. Binary-safe (no string conversion of blob content).
(oid string, dstPath string)
| 700 | } |
| 701 | delay := equalJitter(delays[n]) |
| 702 | logger.InfoContext(ctx, logGitOperationRetrying, "attempt", n+1, "next_attempt", n+2, "backoff_ms", delay.Milliseconds()) |
| 703 | if waitErr := waitForGitRetry(ctx, delay); waitErr != nil { |
| 704 | logInterrupted(operationErr, waitErr) |
| 705 | return operationErr |
| 706 | } |
| 707 | } |
| 708 | } |
| 709 | |
| 710 | func gitOperationCauses(err error) (cause error, contextErr error, retryable bool) { |
| 711 | if commandErr, ok := errors.AsType[*gitCommandError](err); ok { |
| 712 | return commandErr.cause, commandErr.contextErr, commandErr.retryable |
| 713 | } |
| 714 | return err, nil, isTransientGitError(err) |
| 715 | } |
| 716 | |
| 717 | func equalJitter(maximum time.Duration) time.Duration { |
| 718 | if maximum <= 1 { |
| 719 | return maximum |
| 720 | } |
| 721 | half := maximum / 2 |
| 722 | return half + time.Duration(rand.Int64N(int64(maximum-half))) |
| 723 | } |
| 724 | |
| 725 | func waitForGitRetry(ctx context.Context, delay time.Duration) error { |
| 726 | timer := time.NewTimer(delay) |
| 727 | defer timer.Stop() |
| 728 | select { |
| 729 | case <-ctx.Done(): |
| 730 | return ctx.Err() |
| 731 | case <-timer.C: |
| 732 | return nil |
| 733 | } |
| 734 | } |
| 735 | |
| 736 | func isTransientGitError(err error) bool { |
| 737 | if err == nil { |
| 738 | return false |
| 739 | } |
| 740 | return isTransientGitMessage(err.Error()) |
| 741 | } |
| 742 | |
| 743 | var permanentGitFailureSymptoms = []string{ |
| 744 | "no space left on device", |
| 745 | "disk quota exceeded", |
| 746 | "permission denied", |
| 747 | "read-only file system", |
| 748 | } |
| 749 | |
| 750 | var transientGitFailureSymptoms = []string{ |
| 751 | "http 408", "http/1.0 408", "http/1.1 408", "http/2 408", "returned error: 408", |
| 752 | "http 500", "http/1.0 500", "http/1.1 500", "http/2 500", "returned error: 500", |
| 753 | "http 502", "http/1.0 502", "http/1.1 502", "http/2 502", "returned error: 502", |
| 754 | "http 503", "http/1.0 503", "http/1.1 503", "http/2 503", "returned error: 503", |
| 755 | "http 504", "http/1.0 504", "http/1.1 504", "http/2 504", "returned error: 504", |
no test coverage detected