Skip to content

Commit

Permalink
Merge pull request #1005 from dgageot/fix-1004
Browse files Browse the repository at this point in the history
Debounce rapid file changes
  • Loading branch information
dgageot authored Sep 24, 2018
2 parents 1ba8da2 + 6543199 commit c7ff9d7
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 6 deletions.
1 change: 1 addition & 0 deletions cmd/skaffold/app/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ func setFlagsFromEnvVariables(commands []*cobra.Command) {
func AddDevFlags(cmd *cobra.Command) {
cmd.Flags().BoolVar(&opts.Cleanup, "cleanup", true, "Delete deployments after dev mode is interrupted")
cmd.Flags().StringArrayVarP(&opts.Watch, "watch-image", "w", nil, "Choose which artifacts to watch. Artifacts with image names that contain the expression will be watched only. Default is to watch sources for all artifacts.")
cmd.Flags().IntVarP(&opts.WatchPollInterval, "watch-poll-interval", "i", 1000, "Interval (in ms) between two checks for file changes.")
}

func AddRunDeployFlags(cmd *cobra.Command) {
Expand Down
1 change: 1 addition & 0 deletions pkg/skaffold/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type SkaffoldOptions struct {
CustomTag string
Namespace string
Watch []string
WatchPollInterval int
}

// Labels returns a map of labels to be applied to all deployed
Expand Down
5 changes: 2 additions & 3 deletions pkg/skaffold/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ import (
"github.com/sirupsen/logrus"
)

const PollInterval = 1000 * time.Millisecond

// ErrorConfigurationChanged is a special error that's returned when the skaffold configuration was changed.
var ErrorConfigurationChanged = errors.New("configuration changed")

Expand Down Expand Up @@ -315,7 +313,8 @@ func (r *SkaffoldRunner) Dev(ctx context.Context, out io.Writer, artifacts []*v1
return nil, errors.Wrap(err, "starting port-forwarder")
}

return nil, watcher.Run(ctx, PollInterval, onChange)
pollInterval := time.Duration(r.opts.WatchPollInterval) * time.Millisecond
return nil, watcher.Run(ctx, pollInterval, onChange)
}

func (r *SkaffoldRunner) shouldWatch(artifact *v1alpha3.Artifact) bool {
Expand Down
3 changes: 3 additions & 0 deletions pkg/skaffold/runner/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,9 @@ func TestDev(t *testing.T) {
Deployer: test.deployer,
Tagger: &tag.ChecksumTagger{},
watchFactory: test.watcherFactory,
opts: &config.SkaffoldOptions{
WatchPollInterval: 100,
},
}
_, err := runner.Dev(context.Background(), ioutil.Discard, nil)

Expand Down
21 changes: 18 additions & 3 deletions pkg/skaffold/watch/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,30 +65,45 @@ func (w *watchList) Run(ctx context.Context, pollInterval time.Duration, onChang
ticker := time.NewTicker(pollInterval)
defer ticker.Stop()

changedComponents := map[int]bool{}

for {
select {
case <-ctx.Done():
return nil
case <-ticker.C:
changed := 0

for _, component := range *w {
for i, component := range *w {
state, err := stat(component.deps)
if err != nil {
return errors.Wrap(err, "listing files")
}

if hasChanged(component.state, state) {
component.onChange()
changedComponents[i] = true
component.state = state
changed++
}
}

if changed > 0 {
// Rapid file changes that are more frequent than the poll interval would trigger
// multiple rebuilds.
// To prevent that, we debounce changes that happen too quickly
// by waiting for a full turn where nothing happens and trigger a rebuild for
// the accumulated changes.
if changed == 0 && len(changedComponents) > 0 {
for i, component := range *w {
if changedComponents[i] {
component.onChange()
}
}

if err := onChange(); err != nil {
return errors.Wrap(err, "calling final callback")
}

changedComponents = map[int]bool{}
}
}
}
Expand Down

0 comments on commit c7ff9d7

Please sign in to comment.