Skip to content

Commit 3b0742f

Browse files
authored
watch: batch & de-duplicate file events (docker#10865)
Adjust the debouncing logic so that it applies to all inbound file events, regardless of whether they match a sync or rebuild rule. When the batch is flushed out, if any event for the service is a rebuild event, then the service is rebuilt and all sync events for the batch are ignored. If _all_ events in the batch are sync events, then a sync is triggered, passing the entire batch at once. This provides a substantial performance win for the new `tar`-based implementation, as it can efficiently transfer the changes in bulk. Additionally, this helps with jitter, e.g. it's not uncommon for there to be double-writes in quick succession to a file, so even if there's not many files being modified at once, it can still prevent some unnecessary transfers. Signed-off-by: Milas Bowman <[email protected]>
1 parent efd44de commit 3b0742f

File tree

7 files changed

+360
-256
lines changed

7 files changed

+360
-256
lines changed

internal/sync/docker_cp.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,19 @@ func (d *DockerCopy) sync(ctx context.Context, service types.ServiceConfig, path
7171
if fi.IsDir() {
7272
for i := 1; i <= scale; i++ {
7373
_, err := d.client.Exec(ctx, d.projectName, api.RunOptions{
74-
Service: pathMapping.Service,
74+
Service: service.Name,
7575
Command: []string{"mkdir", "-p", pathMapping.ContainerPath},
7676
Index: i,
7777
})
7878
if err != nil {
79-
logrus.Warnf("failed to create %q from %s: %v", pathMapping.ContainerPath, pathMapping.Service, err)
79+
logrus.Warnf("failed to create %q from %s: %v", pathMapping.ContainerPath, service.Name, err)
8080
}
8181
}
8282
fmt.Fprintf(d.infoWriter, "%s created\n", pathMapping.ContainerPath)
8383
} else {
8484
err := d.client.Copy(ctx, d.projectName, api.CopyOptions{
8585
Source: pathMapping.HostPath,
86-
Destination: fmt.Sprintf("%s:%s", pathMapping.Service, pathMapping.ContainerPath),
86+
Destination: fmt.Sprintf("%s:%s", service.Name, pathMapping.ContainerPath),
8787
})
8888
if err != nil {
8989
return err
@@ -93,12 +93,12 @@ func (d *DockerCopy) sync(ctx context.Context, service types.ServiceConfig, path
9393
} else if errors.Is(statErr, fs.ErrNotExist) {
9494
for i := 1; i <= scale; i++ {
9595
_, err := d.client.Exec(ctx, d.projectName, api.RunOptions{
96-
Service: pathMapping.Service,
96+
Service: service.Name,
9797
Command: []string{"rm", "-rf", pathMapping.ContainerPath},
9898
Index: i,
9999
})
100100
if err != nil {
101-
logrus.Warnf("failed to delete %q from %s: %v", pathMapping.ContainerPath, pathMapping.Service, err)
101+
logrus.Warnf("failed to delete %q from %s: %v", pathMapping.ContainerPath, service.Name, err)
102102
}
103103
}
104104
fmt.Fprintf(d.infoWriter, "%s deleted from service\n", pathMapping.ContainerPath)

internal/sync/shared.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ import (
2222

2323
// PathMapping contains the Compose service and modified host system path.
2424
type PathMapping struct {
25-
// Service that the file event is for.
26-
Service string
2725
// HostPath that was created/modified/deleted outside the container.
2826
//
2927
// This is the path as seen from the user's perspective, e.g.

internal/sync/tar.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,7 @@ func (t *Tar) Sync(ctx context.Context, service types.ServiceConfig, paths []Pat
121121
}
122122

123123
type ArchiveBuilder struct {
124-
tw *tar.Writer
125-
paths []string // local paths archived
126-
124+
tw *tar.Writer
127125
// A shared I/O buffer to help with file copying.
128126
copyBuf *bytes.Buffer
129127
}
@@ -168,7 +166,6 @@ func (a *ArchiveBuilder) ArchivePathsIfExist(paths []PathMapping) error {
168166
if err != nil {
169167
return fmt.Errorf("archiving %q: %w", entry.path, err)
170168
}
171-
a.paths = append(a.paths, entry.path)
172169
}
173170
return nil
174171
}

pkg/compose/compose.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import (
2626
"strings"
2727
"sync"
2828

29+
"github.com/jonboulle/clockwork"
30+
2931
"github.com/docker/docker/api/types/volume"
3032

3133
"github.com/compose-spec/compose-go/types"
@@ -58,13 +60,15 @@ func init() {
5860
func NewComposeService(dockerCli command.Cli) api.Service {
5961
return &composeService{
6062
dockerCli: dockerCli,
63+
clock: clockwork.NewRealClock(),
6164
maxConcurrency: -1,
6265
dryRun: false,
6366
}
6467
}
6568

6669
type composeService struct {
6770
dockerCli command.Cli
71+
clock clockwork.Clock
6872
maxConcurrency int
6973
dryRun bool
7074
}

0 commit comments

Comments
 (0)