Merge pull request #38 from entireio/soph/smart-subdivision · Entire
Merge pull request #38 from entireio/soph/smart-subdivision
1782678→main· Soph·2mo ago·2 files·+431 added/-21 removed
Use observed pack size to size post-rejection subdivisions
Changes
internal/strategy/bootstrap
Mbootstrap.go +173/-18
- Mbootstrap_test.go +258/-3
223 unmodified lines
...
273 unmodified lines
// master and nocache-cleanup share ~99% of history).
completedRefs := planner.CopyRefHashMap(p.TargetRefs)
// calibratedBytesPerObject tracks the per-object byte estimate
// updated from observed rejected pushes. Starts at the static
// default (which under-counts blob-heavy repos) and ratchets up as
// 413s reveal that the real bytes/object are much higher than 750.
// Used in checkPackSizeAndSubdivide so subsequent sub-pack fetches
// are pre-emptively split when the calibrated estimate exceeds
// p.TargetMaxPack — saving an entire ~limit-sized wasted upload.
calibratedBytesPerObject := int64(estimatedBytesPerObject)
for _, batch := range batches {
if batch.subsumed {
cmds := []gitproto.PushCommand{{
p.TargetMaxPack = expanded
}
}
}
// checkPackSizeAndSubdivide reads the 12-byte PACK header to get the object
// count, estimates total pack size, and if it exceeds batchLimit, closes the
// reader and calls subdivide(). Returns (nil, nil) when subdivided (caller
// should continue to retry), or (prepended reader, nil) to proceed with push.
//
// bytesPerObject lets the caller use a per-run calibrated value
// instead of the static estimatedBytesPerObject default. Calibrating
// after each rejection (using the bytes that flowed through
// packReadCounter) catches blob-heavy repos where the static 750-byte
// average is 10–20× too low.
func checkPackSizeAndSubdivide(
r io.ReadCloser,
batchLimit int64,
bytesPerObject int64,
subdivide func() bool,
) (io.ReadCloser, error) {
//...
}
func calibrateBytesPerObject(sentBytes, objectCount, current int64) int64 {
if sentBytes <= 0 || objectCount <= 0 {
return 0
}
const safetyMultiplier = 2
calibrated := safetyMultiplier * sentBytes / objectCount
if calibrated <= current {
return 0
}
return calibrated
}
type wrappedMultiRC struct {
io.ReadCloser
}
Test Cases
- TestCalibrateBytesPerObject
- TestSubdivideCheckpoints
- TestObservedSubdivisionFactor
- TestSubdivideToFactorReachesTarget
func TestObservedSubdivisionFactor(t *testing.T) {
t.Parallel()
cases := []struct {
name string
sentBytes int64
limit int64
want int
}{
{
name: "no signal falls back to halving",
want: 2,
},
{
name: "sent comfortably under the limit uses conservative 2x multiplier",
sentBytes: 100, limit: 1000, want: 2,
},
// Other test cases...
}
}
Function Descriptions
checkPackSizeAndSubdivide: Reads PACK header and checks sizes.calibrateBytesPerObject: Estimates per-object sizes based on actual sent bytes.