-
Notifications
You must be signed in to change notification settings - Fork 282
Add timing feature for bid optimization #839
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fbacd4c
22cdda3
57fbab0
00fd8f8
8a477b1
bd833b7
1570efc
7a381b0
c66acfd
0f78e8d
e03871b
a1b5f9d
5886daa
5115d3f
5a266b0
5390fe7
acbb049
fcc765c
6c26d5b
9141779
d099ada
65cc2cf
8ac8fbd
2c9bcc2
b827162
b4464d0
4bbb2d4
064cdeb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| package cli | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
|
|
||
| "github.com/flashbots/mev-boost/server/types" | ||
| "github.com/fsnotify/fsnotify" | ||
| "github.com/sirupsen/logrus" | ||
| "github.com/spf13/viper" | ||
| "gopkg.in/yaml.v3" | ||
| ) | ||
|
|
||
| var errRelayConfiguredTwice = errors.New("relay is specified in both cli flags and config file") | ||
|
|
||
| type RelayConfigYAML struct { | ||
| URL string `yaml:"url"` | ||
| EnableTimingGames bool `yaml:"enable_timing_games"` | ||
| TargetFirstRequestMs uint64 `yaml:"target_first_request_ms"` | ||
| FrequencyGetHeaderMs uint64 `yaml:"frequency_get_header_ms"` | ||
| } | ||
|
|
||
| // Config holds all configuration settings from the config file | ||
| type Config struct { | ||
| TimeoutGetHeaderMs uint64 `yaml:"timeout_get_header_ms"` | ||
| LateInSlotTimeMs uint64 `yaml:"late_in_slot_time_ms"` | ||
| Relays []RelayConfigYAML `yaml:"relays"` | ||
| } | ||
|
|
||
| type ConfigResult struct { | ||
| RelayConfigs map[string]types.RelayConfig | ||
| TimeoutGetHeaderMs uint64 | ||
| LateInSlotTimeMs uint64 | ||
| } | ||
|
|
||
| // ConfigWatcher provides hot reloading of config files | ||
| type ConfigWatcher struct { | ||
| v *viper.Viper | ||
| configPath string | ||
| cliRelays []types.RelayEntry | ||
| onConfigChange func(*ConfigResult) | ||
| log *logrus.Entry | ||
| } | ||
|
|
||
| // LoadConfigFile loads configurations from a YAML file | ||
| func LoadConfigFile(configPath string) (*ConfigResult, error) { | ||
| data, err := os.ReadFile(configPath) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| var config Config | ||
| if err := yaml.Unmarshal(data, &config); err != nil { | ||
| return nil, err | ||
| } | ||
| return parseConfig(config) | ||
| } | ||
|
|
||
| // NewConfigWatcher creates a new config file watcher | ||
| func NewConfigWatcher(configPath string, cliRelays []types.RelayEntry, log *logrus.Entry) (*ConfigWatcher, error) { | ||
| v := viper.New() | ||
| absPath, err := filepath.Abs(configPath) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| v.SetConfigFile(absPath) | ||
| v.SetConfigType("yaml") | ||
|
|
||
| if err := v.ReadInConfig(); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return &ConfigWatcher{ | ||
| v: v, | ||
| configPath: absPath, | ||
| cliRelays: cliRelays, | ||
| log: log, | ||
| }, nil | ||
| } | ||
|
|
||
| // Watch starts watching the config file for changes | ||
| func (cw *ConfigWatcher) Watch(onConfigChange func(*ConfigResult)) { | ||
| cw.onConfigChange = onConfigChange | ||
|
|
||
| cw.v.OnConfigChange(func(_ fsnotify.Event) { | ||
| cw.log.Info("config file changed, reloading...") | ||
|
|
||
| // explicitly read the file to get the latest content since viper | ||
| // may cache the old value and not read the file immediately. | ||
| data, err := os.ReadFile(cw.configPath) | ||
| if err != nil { | ||
| cw.log.WithError(err).Error("failed to read new config file, keeping old config") | ||
| return | ||
| } | ||
|
|
||
| var config Config | ||
| if err := yaml.Unmarshal(data, &config); err != nil { | ||
| cw.log.WithError(err).Error("failed to unmarshal new config, keeping old config") | ||
| return | ||
| } | ||
| newConfig, err := parseConfig(config) | ||
| if err != nil { | ||
| cw.log.WithError(err).Error("failed to parse new config, keeping old config") | ||
| return | ||
| } | ||
|
|
||
| cw.log.Infof("successfully loaded new config with %d relays from config file", len(newConfig.RelayConfigs)) | ||
|
|
||
| if cw.onConfigChange != nil { | ||
| cw.onConfigChange(newConfig) | ||
| } | ||
| }) | ||
|
|
||
| cw.v.WatchConfig() | ||
| } | ||
|
|
||
| // MergeRelayConfigs merges relays passed via --relay with relays from config file. | ||
| // Returns an error if the same relay appears in both places. | ||
| // Users should specify each relay in exactly one place either cli or config file. | ||
| func MergeRelayConfigs(relays []types.RelayEntry, configMap map[string]types.RelayConfig) ([]types.RelayConfig, error) { | ||
| configs := make([]types.RelayConfig, 0) | ||
|
|
||
| for _, entry := range relays { | ||
| urlStr := entry.String() | ||
| if _, exists := configMap[urlStr]; exists { | ||
| return nil, fmt.Errorf("%w: %s", errRelayConfiguredTwice, urlStr) | ||
| } | ||
| configs = append(configs, types.NewRelayConfig(entry)) | ||
| } | ||
|
|
||
| for _, config := range configMap { | ||
| configs = append(configs, config) | ||
| } | ||
|
|
||
| return configs, nil | ||
| } | ||
|
|
||
| func parseConfig(config Config) (*ConfigResult, error) { | ||
| timeoutGetHeaderMs := config.TimeoutGetHeaderMs | ||
| if timeoutGetHeaderMs == 0 { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. neeed to clearly specify this in a documentaton on what the default value is. you can do it in the config.yaml
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we already have doc and default values in the config file. Do you propose some different value? |
||
| timeoutGetHeaderMs = 950 | ||
| } | ||
|
|
||
| lateInSlotTimeMs := config.LateInSlotTimeMs | ||
| if lateInSlotTimeMs == 0 { | ||
| lateInSlotTimeMs = 2000 | ||
| } | ||
|
|
||
| configMap := make(map[string]types.RelayConfig) | ||
| for _, relay := range config.Relays { | ||
| relayEntry, err := types.NewRelayEntry(strings.TrimSpace(relay.URL)) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| relayConfig := types.RelayConfig{ | ||
| RelayEntry: relayEntry, | ||
| EnableTimingGames: relay.EnableTimingGames, | ||
| TargetFirstRequestMs: relay.TargetFirstRequestMs, | ||
| FrequencyGetHeaderMs: relay.FrequencyGetHeaderMs, | ||
| } | ||
| configMap[relayEntry.String()] = relayConfig | ||
| } | ||
|
|
||
| return &ConfigResult{ | ||
| RelayConfigs: configMap, | ||
| TimeoutGetHeaderMs: timeoutGetHeaderMs, | ||
| LateInSlotTimeMs: lateInSlotTimeMs, | ||
| }, nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.