Skip to content

Conversation

@tenfyzhong
Copy link
Collaborator

What problem does this PR solve?

This PR refactors the heartbeatpb.TableSpan struct by introducing a NewTableSpan constructor. This constructor ensures that all fields of TableSpan are properly initialized, preventing potential issues arising from uninitialized fields. This change aims to improve code robustness and maintainability by enforcing consistent initialization patterns for TableSpan objects across the codebase.

Issue Number: close #2689

What is changed and how it works?

The primary change is the addition of the NewTableSpan function in heartbeatpb/table_span.go. This function serves as a factory for creating TableSpan instances, ensuring that TableID, StartKey, EndKey, and KeyspaceID are all explicitly set.

Throughout the codebase, instances of heartbeatpb.TableSpan that were previously created using struct literals (e.g., &heartbeatpb.TableSpan{...}) are now replaced with calls to heartbeatpb.NewTableSpan(...). This systematic replacement ensures that all TableSpan objects are created using the new, safer constructor.

The NewTableSpan constructor is designed to be flexible and can be used to create TableSpan objects with any combination of fields populated, while still ensuring that all fields have a defined zero value if not explicitly provided.

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No code

Questions

Will it cause performance regression or break compatibility?

No, this change should not cause performance regressions. The NewTableSpan constructor is a simple initialization function, and replacing struct literals with constructor calls is a common refactoring that does not impact performance.

This change is backward-compatible as it only affects how TableSpan objects are created internally. Existing code that reads TableSpan objects will continue to function correctly.

Do you need to update user documentation, design documentation or monitoring documentation?

No, this change is an internal refactoring and does not require updates to user-facing documentation, design documents, or monitoring configurations.

Release note

None

Refactors the creation of `heartbeatpb.TableSpan` instances across various packages to consistently use the `heartbeatpb.NewTableSpan` constructor.

This change aims to:
- Improve code readability and maintainability by centralizing span creation logic.
- Ensure consistent initialization of `TableSpan` fields.
- Reduce the likelihood of errors due to missing field initializations.

The following packages and files have been updated:
- `downstreamadapter/dispatcher/event_dispatcher_test.go`
- `downstreamadapter/dispatchermanager/dispatcher_manager_helper.go`
- `downstreamadapter/dispatchermanager/dispatcher_manager_test.go`
- `downstreamadapter/eventcollector/dispatcher_stat_test.go`
- `downstreamadapter/eventcollector/event_collector_test.go`
- `heartbeatpb/table_span.go`
- `logservice/eventstore/event_store_test.go`
- `logservice/logpuller/priority_queue_test.go`
- `logservice/logpuller/region_event_handler_test.go`
- `logservice/logpuller/region_req_cache_test.go`
- `logservice/logpuller/regionlock/region_range_lock.go`
- `logservice/logpuller/regionlock/region_range_lock_test.go`
- `logservice/logpuller/regionlock/util_test.go`
- `logservice/logpuller/subscription_client.go`
- `logservice/logpuller/subscription_client_test.go`
- `logservice/schemastore/ddl_job_fetcher.go`
- `maintainer/barrier_test.go`
- `maintainer/maintainer_controller_bootstrap.go`
- `maintainer/maintainer_controller_helper.go`
- `maintainer/maintainer_controller_test.go`
- `maintainer/operator/operator_add_test.go`
- `maintainer/operator/operator_merge.go`
- `maintainer/operator/operator_merge_test.go`
- `maintainer/operator/operator_split_test.go`
- `maintainer/replica/replication_span.go`
- `maintainer/replica/replication_span_test.go`
- `maintainer/replica/split_span_checker_test.go`
- `maintainer/span/span_controller.go`
- `maintainer/split/region_count_splitter.go`
- `maintainer/split/region_count_splitter_test.go`
- `maintainer/split/splitter_test.go`
- `maintainer/split/write_bytes_splitter.go`
- `maintainer/split/write_bytes_splitter_test.go`
- `maintainer/testutil/test_util.go`
- `pkg/common/format_test.go`
- `pkg/common/span_hash_map.go`
- `pkg/common/span_op.go`
- `pkg/common/span_test.go`
- `pkg/common/table_span.go`
- `pkg/common/table_span_test.go`
- `pkg/eventservice/event_scanner_test.go`
- `pkg/eventservice/event_service_test.go`
- `pkg/pdutil/api_client_test.go`
- `pkg/spanz/btree_map.go`
- `pkg/spanz/btree_map_test.go`
- `pkg/spanz/convert.go`
- `pkg/spanz/convert_test.go`

Signed-off-by: tenfyzhong <[email protected]>
@ti-chi-bot ti-chi-bot bot added the release-note-none Denotes a PR that doesn't merit a release note. label Dec 4, 2025
@ti-chi-bot
Copy link

ti-chi-bot bot commented Dec 4, 2025

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign wlwilliamx for approval. For more information see the Code Review Process.
Please ensure that each of them provides their approval before proceeding.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Dec 4, 2025
@gemini-code-assist
Copy link

Summary of Changes

Hello @tenfyzhong, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the heartbeatpb.TableSpan struct by implementing a dedicated NewTableSpan constructor. This change standardizes how TableSpan objects are created throughout the codebase, ensuring that all fields are explicitly initialized. By migrating existing direct struct literal usages to this new constructor, the PR enhances the overall robustness and maintainability of the code, making it less prone to errors related to uninitialized data.

Highlights

  • New TableSpan Constructor: Introduced a NewTableSpan constructor in heartbeatpb/table_span.go to ensure consistent and proper initialization of TableSpan objects.
  • Codebase Refactoring: Replaced direct struct literal instantiations of heartbeatpb.TableSpan with calls to the new NewTableSpan constructor across numerous files in downstreamadapter, logservice, maintainer, and pkg directories.
  • Improved Robustness and Maintainability: The refactoring aims to prevent potential issues from uninitialized fields and enforce a safer, more maintainable pattern for TableSpan creation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

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

Choose a reason for hiding this comment

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

Code Review

This is a great refactoring that improves code robustness by ensuring TableSpan objects are consistently initialized. The changes are applied thoroughly across the codebase.
I've identified a potential issue in pkg/common/span_hash_map.go and pkg/spanz/convert.go where KeyspaceID is not considered when creating a hashable span, which could lead to bugs when multiple keyspaces are in use.
Additionally, it seems there is some code duplication between pkg/common/span_hash_map.go and pkg/spanz/convert.go regarding hashableSpan and its helper functions. This could be a good opportunity for future cleanup to centralize this logic.

StartKey: unsafeStringToBytes(h.StartKey),
EndKey: unsafeStringToBytes(h.EndKey),
}
span := heartbeatpb.NewTableSpan(h.TableID, unsafeStringToBytes(h.StartKey), unsafeStringToBytes(h.EndKey), 0)

Choose a reason for hiding this comment

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

high

KeyspaceID is hardcoded to 0. This can lead to incorrect behavior when dealing with multiple keyspaces, as spans that are identical except for their TableID, StartKey, and EndKey will be treated as the same, causing hash collisions. The heartbeatpb.TableSpan.Equal method considers KeyspaceID, so the hashable representation should as well.

To fix this, hashableSpan struct should be updated to include KeyspaceID, and toHashableSpan and toSpan functions should be updated to handle it.

For example:

// hashableSpan is a hashable span, which can be used as a map key.
type hashableSpan struct {
	TableID    int64
	StartKey   string
	EndKey     string
	KeyspaceID uint32
}

// toHashableSpan converts a Span to a hashable span.
func toHashableSpan(span heartbeatpb.TableSpan) hashableSpan {
	return hashableSpan{
		TableID:    span.TableID,
		StartKey:   unsafeBytesToString(span.StartKey),
		EndKey:     unsafeBytesToString(span.EndKey),
		KeyspaceID: span.KeyspaceID,
	}
}

// toSpan converts to Span.
func (h hashableSpan) toSpan() heartbeatpb.TableSpan {
	span := heartbeatpb.NewTableSpan(h.TableID, unsafeStringToBytes(h.StartKey), unsafeStringToBytes(h.EndKey), h.KeyspaceID)
	return *span
}

StartKey: unsafeStringToBytes(h.StartKey),
EndKey: unsafeStringToBytes(h.EndKey),
}
span := heartbeatpb.NewTableSpan(h.TableID, unsafeStringToBytes(h.StartKey), unsafeStringToBytes(h.EndKey), 0)

Choose a reason for hiding this comment

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

high

KeyspaceID is hardcoded to 0. This can lead to incorrect behavior when dealing with multiple keyspaces, as spans that are identical except for their TableID, StartKey, and EndKey will be treated as the same, causing hash collisions. The heartbeatpb.TableSpan.Equal method considers KeyspaceID, so the hashable representation should as well.

To fix this, hashableSpan struct should be updated to include KeyspaceID, and toHashableSpan and toSpan functions should be updated to handle it.

For example:

// hashableSpan is a hashable span, which can be used as a map key.
type hashableSpan struct {
	TableID    int64
	StartKey   string
	EndKey     string
	KeyspaceID uint32
}

// toHashableSpan converts a Span to a hashable span.
func toHashableSpan(span heartbeatpb.TableSpan) hashableSpan {
	return hashableSpan{
		TableID:    span.TableID,
		StartKey:   unsafeBytesToString(span.StartKey),
		EndKey:     unsafeBytesToString(span.EndKey),
		KeyspaceID: span.KeyspaceID,
	}
}

// toSpan converts to Span.
func (h hashableSpan) toSpan() heartbeatpb.TableSpan {
	span := heartbeatpb.NewTableSpan(h.TableID, unsafeStringToBytes(h.StartKey), unsafeStringToBytes(h.EndKey), h.KeyspaceID)
	return *span
}

@tenfyzhong tenfyzhong marked this pull request as draft December 16, 2025 03:30
@ti-chi-bot ti-chi-bot bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Dec 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. release-note-none Denotes a PR that doesn't merit a release note. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refactor a dedicated function for creating TableSpan objects

1 participant