Skip to content

feat: Adding Timeline Style Registry - #625

Closed
kyasbal wants to merge 2 commits into
GoogleCloudPlatform:epic/file-schema-v6from
kyasbal:push-tkqzulouwzwu
Closed

feat: Adding Timeline Style Registry#625
kyasbal wants to merge 2 commits into
GoogleCloudPlatform:epic/file-schema-v6from
kyasbal:push-tkqzulouwzwu

Conversation

@kyasbal

@kyasbal kyasbal commented Apr 21, 2026

Copy link
Copy Markdown
Member

Overview

This PR introduces a thread-safe StyleRegistry for the next-generation KHI file format (v6), replacing the hardcoded pkg/model/enum approach. This allows KHI plugins to dynamically register UI styles and ensures global consistency.

Key Changes

  • Implemented pkg/model/khifile/v6/style/timeline.go with thread-safe slice storage and monotonic ID assignment.
  • Added registration functions for TimelineType, Severity, Verb, LogType, and RevisionState.
  • Designed the API to mutate the provided Protobuf pointers (injecting the assigned ID) and return them for inline global variable initialization.
  • Added GenerateChunk() to collect all registered styles into a single TimelineStyleChunk ready for serialization.

Testing

  • Added concurrent unit tests to verify thread safety and unique ID assignment across 100+ goroutines.
  • Verified pointer mutation and chunk generation logic.

@kyasbal kyasbal added the area:backend-core Core backend frameworks, DAG task runner, and server API label Apr 21, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request introduces the KHI v6 file format implementation, including container reading/writing, chunk generation with size-based splitting, and a global style registry for timeline metadata. The review identified several critical improvements: converting mutable global variables to constants for the file format header, mitigating potential memory exhaustion (OOM) by limiting decompression size in the reader, and addressing a missing field in the style chunk generation that is necessary for UI rendering.

Comment on lines +28 to +36
var (
// MagicBytes are the first 3 bytes of a KHI file ("KHI").
MagicBytes = []byte{'K', 'H', 'I'}
// Version is the supported version of the KHI file format.
Version = byte(0x06)

ErrInvalidMagicBytes = errors.New("invalid magic bytes")
ErrUnsupportedVersion = errors.New("unsupported version")
)

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.

medium

The MagicBytes and Version are defined as global variables, which makes them mutable. Since these are fundamental constants of the file format, they should be defined as constants to prevent accidental modification at runtime. In Go, MagicBytes can be defined as a string constant and still be indexed like a byte slice.

Suggested change
var (
// MagicBytes are the first 3 bytes of a KHI file ("KHI").
MagicBytes = []byte{'K', 'H', 'I'}
// Version is the supported version of the KHI file format.
Version = byte(0x06)
ErrInvalidMagicBytes = errors.New("invalid magic bytes")
ErrUnsupportedVersion = errors.New("unsupported version")
)
const (
// MagicBytes are the first 3 bytes of a KHI file ("KHI").
MagicBytes = "KHI"
// Version is the supported version of the KHI file format.
Version = byte(0x06)
)
var (
ErrInvalidMagicBytes = errors.New("invalid magic bytes")
ErrUnsupportedVersion = errors.New("unsupported version")
)

Comment thread pkg/model/khifile/v6/container.go Outdated
}
defer gr.Close()

uncompressed, err := io.ReadAll(gr)

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.

security-medium medium

Using io.ReadAll on a gzip.Reader without a size limit can lead to memory exhaustion (OOM) if the input is a "zip bomb" or contains excessively large decompressed data. Since KHI chunks are expected to be within a reasonable size (e.g., around the 64MB Protobuf limit), consider using io.LimitReader to bound the decompression to a safe maximum.

Comment on lines +106 to +134
func GenerateChunk() *pb.TimelineStyleChunk {
mu.RLock()
defer mu.RUnlock()

// Create shallow copies of the slices to avoid concurrent modification issues
// if someone decides to append while another is iterating over the chunk.
cSeverities := make([]*pb.Severity, len(severities))
copy(cSeverities, severities)

cVerbs := make([]*pb.Verb, len(verbs))
copy(cVerbs, verbs)

cLogTypes := make([]*pb.LogType, len(logTypes))
copy(cLogTypes, logTypes)

cRevisionStates := make([]*pb.RevisionState, len(revisionStates))
copy(cRevisionStates, revisionStates)

cTimelineTypes := make([]*pb.TimelineType, len(timelineTypes))
copy(cTimelineTypes, timelineTypes)

return &pb.TimelineStyleChunk{
Severities: cSeverities,
Verbs: cVerbs,
LogTypes: cLogTypes,
RevisionStates: cRevisionStates,
TimelineTypes: cTimelineTypes,
}
}

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.

medium

The GenerateChunk function is missing the IconAtlas field in the returned TimelineStyleChunk. The IconAtlas is essential for rendering icons in the UI (e.g., for revision states). The registry should include a mechanism to register or set the IconAtlas so it can be included in the generated style chunk for global consistency.

@kyasbal
kyasbal force-pushed the push-tkqzulouwzwu branch from 542ac24 to f6fdf8f Compare April 28, 2026 02:59
@kyasbal kyasbal closed this Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:backend-core Core backend frameworks, DAG task runner, and server API

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant