Skip to content

feat: add rollout op#1868

Draft
toddbaert wants to merge 1 commit intomainfrom
feat/rollout
Draft

feat: add rollout op#1868
toddbaert wants to merge 1 commit intomainfrom
feat/rollout

Conversation

@toddbaert
Copy link
Member

@toddbaert toddbaert commented Feb 6, 2026

Draft implementation of a rollout operator.

For a demo with virtual users in your console, do:

make build-flagd
./demo-rollout.sh

For full details and justification, please see ADR: #1867

@toddbaert toddbaert requested review from a team as code owners February 6, 2026 19:18
@netlify
Copy link

netlify bot commented Feb 6, 2026

Deploy Preview for polite-licorice-3db33c ready!

Name Link
🔨 Latest commit 7c08c99
🔍 Latest deploy log https://app.netlify.com/projects/polite-licorice-3db33c/deploys/699c95324524920008faf75e
😎 Deploy Preview https://deploy-preview-1868--polite-licorice-3db33c.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@dosubot dosubot bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Feb 6, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @toddbaert, 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 implements a new rollout operator, enabling time-based progressive feature rollouts. This operator allows for a controlled, gradual transition of users from one feature variant to another over a defined period, offering enhanced flexibility in feature management and deployment strategies.

Highlights

  • New rollout operator: Introduced a custom JSONLogic operator for time-based progressive feature rollouts, allowing gradual user transitions between variants.
  • Flexible Syntax: The operator supports various syntaxes including shorthand (toVariant), longhand (fromVariant, toVariant), and custom bucketBy expressions for defining rollout behavior.
  • Deterministic Assignment: Utilizes murmur3 hashing to ensure consistent user assignment to variants throughout the rollout period, maintaining fairness and predictability.
  • Comprehensive Testing: Includes extensive unit tests covering functionality, determinism, time progression, and nested JSONLogic evaluation to ensure robustness.
  • Demo and Examples: A new shell script (demo-rollout.sh) and JSON configuration examples (samples/rollout.jsonc) have been added to demonstrate and illustrate the operator's usage.
Changelog
  • core/pkg/evaluator/json.go
    • Registered the new RolloutEvaluationName operator with the JSONLogic resolver, making it available for use in flag configurations.
  • core/pkg/evaluator/rollout.go
    • Added the Rollout struct and its Evaluate method, implementing the core logic for time-based variant assignment.
    • Implemented parseRolloutData to handle various input formats for rollout (shorthand, longhand, custom bucketBy).
    • Included calculateElapsed for determining the current progress of the rollout based on start, end, and current timestamps.
    • Developed determineVariant which uses murmur3 hashing to assign users deterministically based on rollout progress.
    • Added evaluateVariant to correctly handle nested JSONLogic expressions within from and to variant definitions.
  • core/pkg/evaluator/rollout_test.go
    • Added TestRolloutEvaluate with multiple test cases covering different input scenarios, edge cases, and invalid configurations.
    • Included TestRolloutDeterminism to verify consistent variant assignment for the same user across multiple evaluations.
    • Implemented TestRolloutTimeProgression to ensure the percentage of users receiving the new variant increases linearly over time.
    • Added TestRolloutNestedJSONLogic to confirm proper evaluation of JSONLogic expressions within from and to variants.
    • Included TestRolloutIntegration to demonstrate the operator's functionality within the broader flag evaluation context.
  • demo-rollout.sh
    • Created a new bash script to provide a live demonstration of the rollout operator, showing user migration over a 30-second period.
  • samples/rollout.jsonc
    • Added new JSON configuration examples illustrating various ways to use the rollout operator, including shorthand, custom bucketBy, and nested logic.
Activity
  • The pull request introduces a significant new feature: the rollout operator, designed for progressive feature delivery.
  • It includes the complete implementation of the operator, along with extensive unit and integration testing to ensure its reliability.
  • Practical examples and a demonstration script are provided, making it easier for users to understand and adopt the new functionality.
  • The author has linked to an Architectural Decision Record (ADR) for detailed justification and design considerations, indicating a thorough development process.
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.

@toddbaert toddbaert marked this pull request as draft February 6, 2026 19:20
Copy link
Contributor

@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 pull request introduces a new rollout operator for gradual feature flag rollouts over time. The implementation is well-structured and includes thorough tests covering determinism, time progression, and integration. My review focuses on improving robustness and correctness. I've suggested using int64 for timestamps to avoid future overflow issues, adding a necessary check for evaluation context properties, and cleaning up the sample file which contains outdated and incorrect examples. I also included minor suggestions to improve logging practices.

Comment on lines +79 to +81
startTime int32
endTime int32
currentTime int32
Copy link
Contributor

Choose a reason for hiding this comment

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

high

Using int32 for Unix timestamps is risky due to the Year 2038 problem, as they will overflow. Please use int64 for startTime, endTime, and currentTime to ensure the long-term correctness of this feature. This will also require updating extractInt to handle int64 and adjusting the type conversions in parseRolloutData.

Suggested change
startTime int32
endTime int32
currentTime int32
startTime int64
endTime int64
currentTime int64

Comment on lines +114 to +118
properties, _ := getFlagdProperties(dataMap)
config := &rolloutConfig{
currentTime: int32(properties.Timestamp),
data: data,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The boolean return value from getFlagdProperties is being ignored. This could lead to unexpected behavior if the $flagd properties are missing or malformed in the evaluation context. It's safer to check this value and return an error if it's false.

properties, ok := getFlagdProperties(dataMap)
if !ok {
return nil, errors.New("$flagd properties missing or invalid in context")
}
config := &rolloutConfig{
currentTime: int32(properties.Timestamp),
data:        data,
}

func (r *Rollout) Evaluate(values, data any) any {
config, err := r.parseRolloutData(values, data)
if err != nil {
r.Logger.Warn(fmt.Sprintf("parse rollout evaluation data: %v", err))
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Instead of using fmt.Sprintf to format the error into the log message, it's better to use structured logging by passing the error as a separate field (e.g., zap.Error(err)). This improves log parsing and filtering. You will need to add an import for go.uber.org/zap.

Suggested change
r.Logger.Warn(fmt.Sprintf("parse rollout evaluation data: %v", err))
r.Logger.Warn("parse rollout evaluation data", zap.Error(err))

// evaluate nested JSONLogic
result, err := jsonlogic.ApplyInterface(v, data)
if err != nil {
r.Logger.Warn(fmt.Sprintf("error evaluating nested JSONLogic in rollout: %v", err))
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Instead of using fmt.Sprintf to format the error into the log message, it's better to use structured logging by passing the error as a separate field (e.g., zap.Error(err)). This improves log parsing and filtering. You will need to add an import for go.uber.org/zap.

r.Logger.Warn("error evaluating nested JSONLogic in rollout", zap.Error(err))

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant