Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix snapshot mapping merge bug #230

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

Conversation

ValentaTomas
Copy link
Member

@ValentaTomas ValentaTomas commented Jan 7, 2025

There is a bug that sometimes happens during the sandbox pause. This PR fixed it. It will also include tests for the merged mapping.

Description by Callstackai

This PR fixes a bug related to snapshot mapping merging during sandbox pause and includes tests for the merged mapping functionality.

Diagrams of code changes
sequenceDiagram
    participant Client
    participant HeaderMerger
    participant Storage
    participant Validator

    Client->>HeaderMerger: MergeMappings(baseMapping, diffMapping)
    Note over HeaderMerger: Process base and diff mappings
    
    HeaderMerger->>HeaderMerger: Compare mapping blocks
    HeaderMerger->>HeaderMerger: Handle overlaps
    HeaderMerger->>HeaderMerger: Create merged mapping

    HeaderMerger->>Validator: ValidateMappings(mergedMapping)
    Note over Validator: Validate:<br/>1. Contiguous blocks<br/>2. Block size multiples<br/>3. Complete coverage
    
    alt Validation Success
        Validator-->>HeaderMerger: Valid
        HeaderMerger-->>Client: Return merged mappings
    else Validation Error
        Validator-->>HeaderMerger: Error
        HeaderMerger-->>Client: Return error
    end
Loading
Files Changed
FileSummary
packages/orchestrator/cmd/inspect-header/main.goReplaced direct mapping print statements with a call to the new Format method.
packages/orchestrator/cmd/simulate-headers-merge/main.goAdded a new command-line tool for simulating header merges with detailed metadata output.
packages/shared/pkg/storage/header/inspect.goIntroduced the Format method for BuildMap to standardize output formatting.
packages/shared/pkg/storage/header/mapping.goRefactored MergeMappings to improve clarity and correctness in handling mapping overlaps.
packages/shared/pkg/storage/header/mapping_test.goAdded comprehensive tests for the MergeMappings function to ensure correct behavior.

@ValentaTomas ValentaTomas added the bug Something isn't working label Jan 7, 2025
@ValentaTomas ValentaTomas self-assigned this Jan 7, 2025
@ValentaTomas ValentaTomas marked this pull request as ready for review January 7, 2025 23:24
@ValentaTomas ValentaTomas requested a review from jakubno as a code owner January 7, 2025 23:24
Copy link

@callstackai callstackai bot left a comment

Choose a reason for hiding this comment

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

Key Issues

Critical issues include missing null checks for deserialized headers, potential division by zero and overflow errors in calculations, and incorrect conditions for handling overlapping segments, all of which could lead to application crashes or incorrect data processing.

packages/shared/pkg/storage/header/mapping.go Outdated Show resolved Hide resolved
packages/shared/pkg/storage/header/mapping.go Outdated Show resolved Hide resolved
packages/shared/pkg/storage/header/mapping.go Show resolved Hide resolved
packages/shared/pkg/storage/header/mapping.go Show resolved Hide resolved
packages/shared/pkg/storage/header/mapping.go Show resolved Hide resolved
packages/shared/pkg/storage/header/mapping.go Outdated Show resolved Hide resolved
Copy link
Member

@jakubno jakubno left a comment

Choose a reason for hiding this comment

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

Just to make sure I understand it correctly BuildStorageOffset corresponds to the data in the file right, which consists of only the changed blocks for that BuildId, so based on BuildStorageOffset you know where to read at that file.

I didn't check the visualizer. Thanks for tests! I think they are really helpful here. Maybe adding tests for BuildStorageOffset would make sense as well.

packages/shared/pkg/storage/header/mapping.go Outdated Show resolved Hide resolved
packages/shared/pkg/storage/header/mapping.go Outdated Show resolved Hide resolved
packages/shared/pkg/storage/header/mapping.go Show resolved Hide resolved
packages/shared/pkg/storage/header/mapping.go Outdated Show resolved Hide resolved
packages/shared/pkg/storage/header/mapping_test.go Outdated Show resolved Hide resolved
@ValentaTomas
Copy link
Member Author

ValentaTomas commented Jan 10, 2025

Just to make sure I understand it correctly BuildStorageOffset corresponds to the data in the file right, which consists of only the changed blocks for that BuildId, so based on BuildStorageOffset you know where to read at that file.

Yes.

I didn't check the visualizer. Thanks for tests! I think they are really helpful here.

Maybe adding tests for BuildStorageOffset would make sense as well.
Not 100% sure how to add this, because it basically just checks that the buildStorage file is
long enough to accommodate the mapped offset. Will think about this though,

Copy link

@callstackai callstackai bot left a comment

Choose a reason for hiding this comment

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

Key Issues

The code lacks validation for a non-zero blockSize, which can lead to division errors and potential crashes. Additionally, it assumes mapping.Length and mapping.Offset are multiples of blockSize, risking incorrect calculations and data loss if this assumption is violated.

// It is used for debugging and visualization.
//
// You can pass maps to visualize different groups of buildIds.
func Visualize(mappings []*BuildMap, size, blockSize, cols uint64, bottomGroup, topGroup *map[uuid.UUID]struct{}) string {
Copy link

Choose a reason for hiding this comment

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

🐛 Possible Bug
The code assumes blockSize is non-zero but there's no validation. A zero blockSize will cause a panic in multiple divisions (size/blockSize, mapping.Length/blockSize, mapping.Offset/blockSize).

Suggested change
func Visualize(mappings []*BuildMap, size, blockSize, cols uint64, bottomGroup, topGroup *map[uuid.UUID]struct{}) string {
func Visualize(mappings []*BuildMap, size, blockSize, cols uint64, bottomGroup, topGroup *map[uuid.UUID]struct{}) string {
if blockSize == 0 {
return ""
}

}

for _, mapping := range mappings {
for block := uint64(0); block < mapping.Length/blockSize; block++ {
Copy link

Choose a reason for hiding this comment

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

🐛 Possible Bug
The code assumes mapping.Length and mapping.Offset are multiples of blockSize, which if not true could lead to incorrect block calculations and potential data loss.

Suggested change
for block := uint64(0); block < mapping.Length/blockSize; block++ {
if mapping.Length%blockSize != 0 || mapping.Offset%blockSize != 0 {
continue
}
for block := uint64(0); block < mapping.Length/blockSize; block++ {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants