Skip to content
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

feat: improve ftl dev rebuild algorithm #932

Merged
merged 1 commit into from
Feb 13, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions cmd/ftl/cmd_dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,28 @@ import (
)

type moduleFolderInfo struct {
moduleName string
fileHashes map[string][]byte
schema *schema.Module
moduleName string
schema *schema.Module
forceRebuild bool
}

type devCmd struct {
BaseDir string `arg:"" help:"Directory to watch for FTL modules" type:"existingdir" default:"."`
Watch time.Duration `help:"Watch template directory at this frequency and regenerate on change." default:"500ms"`
FailureDelay time.Duration `help:"Delay before retrying a failed deploy." default:"5s"`
FailureDelay time.Duration `help:"Delay before retrying a failed deploy." default:"500ms"`
ReconnectDelay time.Duration `help:"Delay before attempting to reconnect to FTL." default:"1s"`
ExitAfterDeploy bool `help:"Exit after all modules are deployed successfully." default:"false"`
}

type moduleMap map[string]*moduleFolderInfo

func (m *moduleMap) ForceRebuild(dir string) {
(*m)[dir].fileHashes = make(map[string][]byte)
(*m)[dir].forceRebuild = true
}

func (m *moduleMap) AddModule(dir string, module string) {
(*m)[dir] = &moduleFolderInfo{
moduleName: module,
fileHashes: make(map[string][]byte),
}
}

Expand Down Expand Up @@ -111,6 +110,11 @@ func (d *devCmd) Run(ctx context.Context, client ftlv1connect.ControllerServiceC
return d.watchForSchemaChanges(ctx, client, schemaChanges)
})

previousFailures := 0

// Map of module directory to file hashes
fileHashes := map[string]map[string][]byte{}

for {
logger.Tracef("Scanning %s for FTL module changes", d.BaseDir)
delay := d.Watch
Expand All @@ -127,14 +131,22 @@ func (d *devCmd) Run(ctx context.Context, client ftlv1connect.ControllerServiceC

allModulesDeployed := true

failedModules := map[string]bool{}

for dir := range modules {
currentModule := modules[dir]
hashes, err := d.computeFileHashes(ctx, dir)
if err != nil {
return err
}

if !compareFileHashes(ctx, currentModule.fileHashes, hashes) {
if currentModule.forceRebuild || !compareFileHashes(ctx, fileHashes[dir], hashes) {
if currentModule.forceRebuild {
logger.Debugf("Forcing rebuild of module %s", dir)
currentModule.forceRebuild = false
} else {
logger.Debugf("Detected change in module %s", dir)
}
deploy := deployCmd{
Replicas: 1,
ModuleDir: dir,
Expand All @@ -143,15 +155,22 @@ func (d *devCmd) Run(ctx context.Context, client ftlv1connect.ControllerServiceC
err = deploy.Run(ctx, client)
if err != nil {
logger.Errorf(err, "Error deploying module %s. Will retry", dir)
modules.RemoveModule(dir)
failedModules[dir] = true
// Increase delay when there's a compile failure.
delay = d.FailureDelay
allModulesDeployed = false
} else {
currentModule.fileHashes = hashes
modules.SetModule(dir, currentModule)
}
}
fileHashes[dir] = hashes
}
if previousFailures != len(failedModules) || len(modules) == 0 {
logger.Debugf("Detected %d failed modules, previously had %d", len(failedModules), previousFailures)
for module := range failedModules {
modules.ForceRebuild(module)
}
previousFailures = len(failedModules)
}

if allModulesDeployed && d.ExitAfterDeploy {
Expand Down