Skip to content

Commit

Permalink
feat: throttle flag toggle (#243)
Browse files Browse the repository at this point in the history
Throttle flag toggle to prevent rate-limiting
  • Loading branch information
dbolson authored May 6, 2024
1 parent 005fe8e commit 3b88329
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 4 deletions.
9 changes: 6 additions & 3 deletions internal/quickstart/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package quickstart

import (
"fmt"
"log"
"time"

"github.com/charmbracelet/bubbles/key"
Expand Down Expand Up @@ -172,7 +171,11 @@ func (m ContainerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case fetchedFlagStatusMsg:
m.currentModel, cmd = m.currentModel.Update(msg)
m.err = nil
case fetchedSDKInstructionsMsg, selectedSDKMsg, spinner.TickMsg, createdFlagMsg:
case createdFlagMsg,
fetchedSDKInstructionsMsg,
flagToggleThrottleMsg,
selectedSDKMsg,
spinner.TickMsg:
m.gettingStarted = false
m.currentModel, cmd = m.currentModel.Update(msg)
m.err = nil
Expand All @@ -197,7 +200,7 @@ func (m ContainerModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.currentStep += 1
sendEvent = true
default:
log.Printf("container default: %T\n", msg)
// ignore other messages
}

if sendEvent {
Expand Down
8 changes: 8 additions & 0 deletions internal/quickstart/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,11 @@ func trackSetupFlagToggledEvent(tracker analytics.Tracker, accessToken, baseURI
return eventTrackedMsg{}
}
}

type flagToggleThrottleMsg int

func throttleFlagToggle(count int) tea.Cmd {
return tea.Tick(throttleDuration, func(_ time.Time) tea.Msg {
return flagToggleThrottleMsg(count)
})
}
36 changes: 35 additions & 1 deletion internal/quickstart/toggle_flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package quickstart

import (
"fmt"
"time"

"github.com/charmbracelet/bubbles/help"
"github.com/charmbracelet/bubbles/key"
Expand All @@ -13,6 +14,11 @@ import (
"ldcli/internal/flags"
)

const (
resetThrottleCountThreshold = 1
throttleDuration = time.Second
)

type toggleFlagModel struct {
accessToken string
baseUri string
Expand All @@ -26,6 +32,14 @@ type toggleFlagModel struct {
helpKeys keyMap
sdkKind string
spinner spinner.Model

// Throttling fields to control how quickly a user can press (or hold) tab to toggle the flag.
// We publish a message based on the throttleDuration that increments a counter to control when
// we disable the toggle. We also keep track of how many times we've published a command to toggle
// the flag within the throttleDuration timeframe, resetting it once we've stopped throttling.
resetThrottleCount int // incremented when the user toggles the flag which is only allowed when it's below a threshold
throttleCount int // syncs messages to know when the throttleDuration has elapsed
throttling bool // flag to decide when the user is throttled
}

func NewToggleFlagModel(client flags.Client, accessToken string, baseUri string, flagKey string, sdkKind string) tea.Model {
Expand Down Expand Up @@ -67,14 +81,34 @@ func (m toggleFlagModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if !m.flagWasFetched {
return m, nil
}
if m.throttling || m.resetThrottleCount > resetThrottleCountThreshold {
// don't toggle the flag if we're currently debouncing the user
return m, throttleFlagToggle(m.throttleCount)
}

m.flagWasEnabled = true
m.enabled = !m.enabled
m.err = nil
return m, toggleFlag(m.client, m.accessToken, m.baseUri, m.flagKey, m.enabled)
m.throttleCount += 1
cmd = tea.Sequence(
toggleFlag(m.client, m.accessToken, m.baseUri, m.flagKey, m.enabled),
throttleFlagToggle(m.throttleCount),
)
}
case toggledFlagMsg:
m.resetThrottleCount += 1
case fetchedFlagStatusMsg:
m.enabled = msg.enabled
m.flagWasFetched = true
case flagToggleThrottleMsg:
// if the value on the model is not the same as the message, we know there will be
// additional messages coming. Once they are equal, we know the throttle time has elapsed.
if int(msg) == m.throttleCount {
m.throttling = false
m.resetThrottleCount = 0
} else {
m.throttling = true
}
case spinner.TickMsg:
m.spinner, cmd = m.spinner.Update(msg)
case errMsg:
Expand Down

0 comments on commit 3b88329

Please sign in to comment.