Skip to content

fix(channel): handle dynamic frequency updates - #2002

Merged
seefs001 merged 3 commits into
QuantumNous:mainfrom
qixing-jk:fix/dynamic-frequency-updates
Oct 10, 2025
Merged

fix(channel): handle dynamic frequency updates#2002
seefs001 merged 3 commits into
QuantumNous:mainfrom
qixing-jk:fix/dynamic-frequency-updates

Conversation

@qixing-jk

@qixing-jk qixing-jk commented Oct 10, 2025

Copy link
Copy Markdown
Contributor
  • replace infinite sleep loop with time.Ticker to avoid goroutine leaks
  • add immediate initial test execution before ticker starts
  • implement frequency change detection and ticker recreation
  • ensure proper ticker cleanup when loop exits or feature disabled

Summary by CodeRabbit

  • Bug Fixes
    • Frequency is re-read each inner loop iteration so scheduling adapts to changes immediately.
    • Interval values are logged for each run and a completion message is logged after each automated channel test.
    • Loop now checks at runtime and stops promptly if automatic channel testing is disabled, preventing blocking.

- replace infinite sleep loop with time.Ticker to avoid goroutine leaks
- add immediate initial test execution before ticker starts
- implement frequency change detection and ticker recreation
- ensure proper ticker cleanup when loop exits or feature disabled
@coderabbitai

coderabbitai Bot commented Oct 10, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Moves frequency retrieval for AutoTestChannelMinutes into the inner loop of the channel-test scheduler, logs the interval each inner iteration, adds a log after each testAllChannels run, and checks AutoTestChannelEnabled at runtime to break the inner loop when disabled.

Changes

Cohort / File(s) Summary of Changes
Channel test loop tweaks
controller/channel-test.go
Move retrieval of AutoTestChannelMinutes into the inner loop so frequency is re-read each iteration; log the interval inside the inner loop; add a log message after each testAllChannels run ("automatically channel test finished"); add a runtime check inside the inner loop to break out when AutoTestChannelEnabled is cleared.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant G as Goroutine (scheduler)
  participant Conf as Config (enabled / minutes)
  participant T as Ticker
  participant R as testAllChannels
  Note over G,Conf: Outer loop reads enabled flag once, enters inner loop when enabled
  G->>Conf: read AutoTestChannelEnabled
  alt enabled
    G->>R: run testAllChannels
    R-->>G: done
    G->>G: log "automatically channel test finished"
    G->>T: create ticker
    loop each tick
      T-->>G: tick
      G->>Conf: re-read AutoTestChannelMinutes
      G->>G: log current interval
      G->>Conf: re-check AutoTestChannelEnabled
      alt disabled
        G-->>T: stop ticker
        G-->>G: break inner loop
      else enabled
        G->>R: run testAllChannels
        R-->>G: done
        G->>G: log "automatically channel test finished"
        alt frequency changed
          G-->>T: stop ticker
          G->>T: recreate ticker(new freq)
        end
      end
    end
  else disabled
    G-->>G: exit (no-op)
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

I nibble at loops in a moonlit code glen,
I check the pace, then test channels again.
Each tick I log, and if flags say “pause,” I go,
I hop through the minutes, precise and slow.
Carrots, checks, and tiny logs — cheers from a CodeRabbit! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly and concisely describes the main purpose of the changeset by indicating that the channel logic now supports dynamic frequency updates, matching the primary modification of adjusting ticker behavior and handling runtime configuration changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
controller/channel-test.go (1)

618-663: No graceful shutdown mechanism.

The goroutine spawned at line 620 runs indefinitely with no way to cancel it during application shutdown except by setting AutoTestChannelEnabled to false. This can delay shutdown or leave the goroutine running until the next sleep/tick completes.

Accept a context.Context parameter and monitor it for cancellation:

-func AutomaticallyTestChannels() {
+func AutomaticallyTestChannels(ctx context.Context) {
   autoTestChannelsOnce.Do(func() {
     go func() {
       for {
+        select {
+        case <-ctx.Done():
+          common.SysLog("automatic channel testing stopped via context cancellation")
+          return
+        default:
+        }
+
         if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
           time.Sleep(1 * time.Minute)
           continue
         }
         
         // ... rest of the logic
         
         func() {
           defer ticker.Stop()
           
           // ... 
           
           for {
             select {
+            case <-ctx.Done():
+              common.SysLog("automatic channel testing stopped via context cancellation")
+              return
             case <-ticker.C:
               // ...
             }
           }
         }()
       }
     }()
   })
 }

Then call it from your initialization code with a cancellable context:

ctx, cancel := context.WithCancel(context.Background())
defer cancel() // cancel on shutdown
AutomaticallyTestChannels(ctx)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 721357b and e19e904.

📒 Files selected for processing (1)
  • controller/channel-test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
controller/channel-test.go (2)
setting/operation_setting/monitor_setting.go (1)
  • GetMonitorSetting (25-34)
common/sys_log.go (1)
  • SysLog (11-14)
🔇 Additional comments (2)
controller/channel-test.go (2)

631-659: Ticker lifecycle management looks correct.

The anonymous function pattern with defer ticker.Stop() (line 633) ensures the ticker is properly cleaned up when the function returns due to configuration changes (lines 646, 650) or when the loop exits. This prevents goroutine and ticker leaks.


635-638: Good: immediate initial test execution.

Running the test immediately before starting the ticker loop ensures tests begin without waiting for the first interval to elapse, which improves user experience when the feature is enabled.

Comment on lines 621 to 625
for {
time.Sleep(time.Duration(frequency) * time.Minute)
common.SysLog("automatically testing all channels")
_ = testAllChannels(false)
common.SysLog("automatically channel test finished")
if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
break
time.Sleep(10 * time.Minute)
continue
}

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.

⚠️ Potential issue | 🟡 Minor

Slow reaction time when re-enabling the feature.

When AutoTestChannelEnabled is false, the code sleeps for a hardcoded 10 minutes before rechecking. If an operator re-enables the feature, they must wait up to 10 minutes before tests resume.

Consider reducing the sleep interval (e.g., 1 minute) or using a configuration change notification mechanism for faster response:

 for {
   if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
-    time.Sleep(10 * time.Minute)
+    time.Sleep(1 * time.Minute)
     continue
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for {
time.Sleep(time.Duration(frequency) * time.Minute)
common.SysLog("automatically testing all channels")
_ = testAllChannels(false)
common.SysLog("automatically channel test finished")
if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
break
time.Sleep(10 * time.Minute)
continue
}
for {
if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
time.Sleep(1 * time.Minute)
continue
}
🤖 Prompt for AI Agents
In controller/channel-test.go around lines 621-625, the loop sleeps for a
hardcoded 10 minutes when AutoTestChannelEnabled is false causing slow reaction
to re-enabling; change this to either poll more frequently (e.g., sleep 1
minute) or, preferably, replace the sleep with a configurable interval from
operation_setting (e.g., MonitorSetting.AutoTestPollInterval) or a notification
mechanism: implement a ticker/select that checks the flag at the shorter
interval (or listens on a config change channel) so the loop can exit or resume
promptly when AutoTestChannelEnabled flips.

Comment thread controller/channel-test.go Outdated
Comment on lines +643 to +651
case <-ticker.C:
// 检查是否需要退出或重新配置
if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
return
}
newFrequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
if newFrequency != frequency {
return
}

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.

⚠️ Potential issue | 🟠 Major

Frequency changes are not detected immediately.

The frequency change detection (lines 648-650) only runs after the ticker fires, meaning if the frequency changes from 60 minutes to 5 minutes, the system continues using the old 60-minute interval until the next tick occurs. This delays the effect of dynamic frequency updates by up to one full interval of the old frequency.

For truly dynamic updates, consider one of these approaches:

  1. Poll configuration more frequently (e.g., every minute) in a separate select case
  2. Use a configuration change notification channel to trigger immediate recreation
  3. Document this behavior if delayed reaction is acceptable

Example using periodic polling:

+configCheckTicker := time.NewTicker(1 * time.Minute)
+defer configCheckTicker.Stop()
+
 for {
   select {
   case <-ticker.C:
-    // 检查是否需要退出或重新配置
-    if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
-      return
-    }
-    newFrequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
-    if newFrequency != frequency {
-      return
-    }
-
     // 执行定时任务
     common.SysLog("automatically testing all channels")
     _ = testAllChannels(false)
     common.SysLog("automatically channel test finished")
+
+  case <-configCheckTicker.C:
+    // 检查是否需要退出或重新配置
+    if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
+      return
+    }
+    newFrequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
+    if newFrequency != frequency {
+      common.SysLog(fmt.Sprintf("frequency changed from %d to %d minutes, recreating ticker", frequency, newFrequency))
+      return
+    }
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case <-ticker.C:
// 检查是否需要退出或重新配置
if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
return
}
newFrequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
if newFrequency != frequency {
return
}
// 在进入循环前增加一个更频繁的配置检查
configCheckTicker := time.NewTicker(1 * time.Minute)
defer configCheckTicker.Stop()
for {
select {
case <-ticker.C:
// 执行定时任务
common.SysLog("automatically testing all channels")
_ = testAllChannels(false)
common.SysLog("automatically channel test finished")
case <-configCheckTicker.C:
// 检查是否需要退出或重新配置
if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
return
}
newFrequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
if newFrequency != frequency {
common.SysLog(fmt.Sprintf(
"frequency changed from %d to %d minutes, recreating ticker",
frequency, newFrequency,
))
return
}
}
}

@seefs001

Copy link
Copy Markdown
Collaborator

感觉你搞复杂了,直接把那个获取时间的和日志挪进去内层循环不就可以嘛

@seefs001
seefs001 merged commit df0ae92 into QuantumNous:main Oct 10, 2025
1 check was pending

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
controller/channel-test.go (1)

626-634: Frequency changes take effect only after the current interval completes.

While the code retrieves frequency on each iteration (line 626), it doesn't check whether the frequency has changed during the sleep. If the frequency updates from 60 minutes to 5 minutes, the system continues waiting the full 60 minutes before the next test, then adopts 5 minutes for subsequent iterations.

Impact: Operators must wait up to one full interval (at the old frequency) before dynamic frequency updates take effect, contradicting the PR objective: "Implement detection of frequency changes and recreation of the ticker when frequency updates occur."

Based on learnings from the past review comment, consider polling the configuration more frequently to detect changes:

 for {
   frequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
+  common.SysLog(fmt.Sprintf("automatically test channels with interval %d minutes", frequency))
+  common.SysLog("automatically testing all channels")
+  _ = testAllChannels(false)
+  common.SysLog("automatically channel test finished")
+  
+  // Poll for config changes every minute during the sleep period
+  sleepRemaining := time.Duration(frequency) * time.Minute
+  pollInterval := 1 * time.Minute
+  for sleepRemaining > 0 {
+    if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
+      break
+    }
+    newFrequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
+    if newFrequency != frequency {
+      common.SysLog(fmt.Sprintf("frequency changed from %d to %d minutes, restarting", frequency, newFrequency))
+      break
+    }
+    sleepDuration := pollInterval
+    if sleepRemaining < pollInterval {
+      sleepDuration = sleepRemaining
+    }
+    time.Sleep(sleepDuration)
+    sleepRemaining -= sleepDuration
+  }
-  time.Sleep(time.Duration(frequency) * time.Minute)
-  common.SysLog(fmt.Sprintf("automatically test channels with interval %d minutes", frequency))
-  common.SysLog("automatically testing all channels")
-  _ = testAllChannels(false)
-  common.SysLog("automatically channel test finished")
   if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
     break
   }
 }

This approach also enables faster detection when the feature is disabled mid-interval.

♻️ Duplicate comments (1)
controller/channel-test.go (1)

622-622: Slow reaction time when re-enabling the feature remains unaddressed.

The 10-minute sleep when AutoTestChannelEnabled is false (line 622) was flagged in a previous review as causing delays of up to 10 minutes before tests resume after re-enabling the feature. This concern has not been addressed in the current changes.

Per the past review feedback, consider reducing the sleep interval to 1 minute for faster response:

 if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
-  time.Sleep(10 * time.Minute)
+  time.Sleep(1 * time.Minute)
   continue
 }

Based on learnings from past review.

🧹 Nitpick comments (1)
controller/channel-test.go (1)

628-628: Log timing could be clearer.

The log "automatically test channels with interval X minutes" appears after the sleep but before the test execution, which creates a slightly confusing timeline. If the sleep is moved to the end of the loop (as suggested in the critical issue above), this log would naturally appear at the start of each iteration, providing clearer context: "Starting new cycle with X-minute interval."

This will be automatically resolved if the sleep is moved to the end of the loop as recommended.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7351480 and 57e5d67.

📒 Files selected for processing (1)
  • controller/channel-test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
controller/channel-test.go (2)
setting/operation_setting/monitor_setting.go (1)
  • GetMonitorSetting (25-34)
common/sys_log.go (1)
  • SysLog (11-14)

Comment on lines +626 to +628
frequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
time.Sleep(time.Duration(frequency) * time.Minute)
common.SysLog(fmt.Sprintf("automatically test channels with interval %d minutes", frequency))

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.

⚠️ Potential issue | 🔴 Critical

Critical: Sleep-before-test violates the "immediate initial test" objective.

The current implementation retrieves the frequency and immediately sleeps (lines 626-627) before performing any test. This means:

  1. No immediate initial test: When the inner loop starts, it waits the full interval before the first test runs, contradicting the PR description which explicitly states "Add immediate initial test execution before the ticker starts."
  2. Delayed first execution: If the frequency is 60 minutes, operators must wait a full hour before the first test, even though the feature was just enabled.

The correct flow should be: test → sleep → test, not sleep → test. Move the sleep to the end of the loop:

 for {
   frequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
-  time.Sleep(time.Duration(frequency) * time.Minute)
   common.SysLog(fmt.Sprintf("automatically test channels with interval %d minutes", frequency))
   common.SysLog("automatically testing all channels")
   _ = testAllChannels(false)
   common.SysLog("automatically channel test finished")
   if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
     break
   }
+  time.Sleep(time.Duration(frequency) * time.Minute)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
frequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
time.Sleep(time.Duration(frequency) * time.Minute)
common.SysLog(fmt.Sprintf("automatically test channels with interval %d minutes", frequency))
for {
frequency := operation_setting.GetMonitorSetting().AutoTestChannelMinutes
common.SysLog(fmt.Sprintf("automatically test channels with interval %d minutes", frequency))
common.SysLog("automatically testing all channels")
_ = testAllChannels(false)
common.SysLog("automatically channel test finished")
if !operation_setting.GetMonitorSetting().AutoTestChannelEnabled {
break
}
time.Sleep(time.Duration(frequency) * time.Minute)
}
🤖 Prompt for AI Agents
In controller/channel-test.go around lines 626 to 628, the loop currently reads
the frequency and immediately calls time.Sleep before performing the test, which
prevents an immediate initial test; change the loop order so the test logic runs
first, then call time.Sleep(time.Duration(frequency) * time.Minute) at the end
of the loop (keeping frequency retrieval as-is or validating it once outside the
loop), and ensure you handle zero/negative frequency (skip sleeping or use a
sensible default) so the execution sequence becomes: test → sleep → test.

ennnnny pushed a commit to ennnnny/new-api that referenced this pull request Mar 17, 2026
…ncy-updates

fix(channel): handle dynamic frequency updates
@coderabbitai coderabbitai Bot mentioned this pull request Jun 23, 2026
11 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants