Skip to content

Commit

Permalink
Allow shutting down as an analysis cache clear strategy (#32)
Browse files Browse the repository at this point in the history
We've seen instances in the wild where shutdown can be minutes faster,
due to not invalidating a bunch of python whl_library repositories.

None of this is as ideal as cquery reporting just the information it got
from its particular run, but both of these heuristics may be useful.
Ideally we could get rid of them both at some point.
  • Loading branch information
illicitonion authored Nov 9, 2022
1 parent 61d25f5 commit 7d4f40c
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 46 deletions.
44 changes: 24 additions & 20 deletions cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,14 @@ func (e *EnforceCleanFlag) Set(value string) error {
}

type CommonFlags struct {
WorkingDirectory *string
BazelPath *string
BazelStartupOpts *MultipleStrings
EnforceCleanRepo EnforceCleanFlag
DeleteCachedWorktree bool
IgnoredFiles *IgnoreFileFlag
TargetsFlag *string
WorkingDirectory *string
BazelPath *string
BazelStartupOpts *MultipleStrings
EnforceCleanRepo EnforceCleanFlag
DeleteCachedWorktree bool
IgnoredFiles *IgnoreFileFlag
TargetsFlag *string
AnalysisCacheClearStrategy *string
}

func StrPtr() *string {
Expand All @@ -81,13 +82,14 @@ func StrPtr() *string {

func RegisterCommonFlags() *CommonFlags {
commonFlags := CommonFlags{
WorkingDirectory: StrPtr(),
BazelPath: StrPtr(),
BazelStartupOpts: &MultipleStrings{},
EnforceCleanRepo: AllowIgnored,
DeleteCachedWorktree: false,
IgnoredFiles: &IgnoreFileFlag{},
TargetsFlag: StrPtr(),
WorkingDirectory: StrPtr(),
BazelPath: StrPtr(),
BazelStartupOpts: &MultipleStrings{},
EnforceCleanRepo: AllowIgnored,
DeleteCachedWorktree: false,
IgnoredFiles: &IgnoreFileFlag{},
TargetsFlag: StrPtr(),
AnalysisCacheClearStrategy: StrPtr(),
}
flag.StringVar(commonFlags.WorkingDirectory, "working-directory", ".", "Working directory to query.")
flag.StringVar(commonFlags.BazelPath, "bazel", "bazel",
Expand All @@ -102,6 +104,7 @@ func RegisterCommonFlags() *CommonFlags {
"Files to ignore for git operations, relative to the working-directory. These files shan't affect the Bazel graph.")
flag.StringVar(commonFlags.TargetsFlag, "targets", "//...",
"Targets to consider. Accepts any valid `bazel query` expression (see https://bazel.build/reference/query).")
flag.StringVar(commonFlags.AnalysisCacheClearStrategy, "analysis-cache-clear-strategy", "shutdown", "Strategy for clearing the analysis cache. Accept values: shutdown,discard.")
return &commonFlags
}

Expand Down Expand Up @@ -151,12 +154,13 @@ func ResolveCommonConfig(commonFlags *CommonFlags, beforeRevStr string) (*Common
}

context := &pkg.Context{
WorkspacePath: workingDirectory,
OriginalRevision: afterRev,
BazelCmd: bazelCmd,
BazelOutputBase: outputBase,
DeleteCachedWorktree: commonFlags.DeleteCachedWorktree,
IgnoredFiles: *commonFlags.IgnoredFiles,
WorkspacePath: workingDirectory,
OriginalRevision: afterRev,
BazelCmd: bazelCmd,
BazelOutputBase: outputBase,
DeleteCachedWorktree: commonFlags.DeleteCachedWorktree,
IgnoredFiles: *commonFlags.IgnoredFiles,
AnalysisCacheClearStrategy: *commonFlags.AnalysisCacheClearStrategy,
}

// Non-context attributes
Expand Down
71 changes: 45 additions & 26 deletions pkg/target_determinator.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ type Context struct {
DeleteCachedWorktree bool
// IgnoredFiles represents files that should be ignored for git operations.
IgnoredFiles []common.RelPath
// AnalysisCacheClearStrategy is the strategy used for clearing the Bazel analysis cache before cquery runs.
// Accepted values are: shutdown, discard.
//
// shutdown will shut down the bazel server before queries.
// discard will run a build with --discard_analysis_cache before queries.
//
// discard avoids a potentially costly JVM tear-down and start-up,
/// but seems to over-invalidate things (e.g. it seems to force re-fetching every rules_python whl_library which can be very expensive).
AnalysisCacheClearStrategy string
}

// FullyProcess returns the before and after metadata maps, with fully filled caches.
Expand Down Expand Up @@ -171,12 +180,13 @@ func fullyProcessRevision(context *Context, rev LabelledGitRev, targets TargetsL
func LoadIncompleteMetadata(context *Context, rev LabelledGitRev, targets TargetsList) (*QueryResults, func(), error) {
// Create a temporary context to allow the workspace path to point to a git worktree if necessary.
context = &Context{
WorkspacePath: context.WorkspacePath,
OriginalRevision: context.OriginalRevision,
BazelCmd: context.BazelCmd,
BazelOutputBase: context.BazelOutputBase,
DeleteCachedWorktree: context.DeleteCachedWorktree,
IgnoredFiles: context.IgnoredFiles,
WorkspacePath: context.WorkspacePath,
OriginalRevision: context.OriginalRevision,
BazelCmd: context.BazelCmd,
BazelOutputBase: context.BazelOutputBase,
DeleteCachedWorktree: context.DeleteCachedWorktree,
IgnoredFiles: context.IgnoredFiles,
AnalysisCacheClearStrategy: context.AnalysisCacheClearStrategy,
}
cleanupFunc := func() {}

Expand Down Expand Up @@ -526,34 +536,43 @@ type LabelAndConfiguration struct {
}

func clearAnalysisCache(context *Context) error {
// Discard the analysis cache:
{
var stderr bytes.Buffer
if context.AnalysisCacheClearStrategy == "shutdown" {
result, err := context.BazelCmd.Execute(BazelCmdConfig{Dir: context.WorkspacePath}, "--output_base", context.BazelOutputBase, "shutdown")
if result != 0 || err != nil {
return fmt.Errorf("failed to discard Bazel analysis cache in %v", context.WorkspacePath)
}
return nil
} else if context.AnalysisCacheClearStrategy == "discard" {
{
var stderr bytes.Buffer

result, err := context.BazelCmd.Execute(
BazelCmdConfig{Dir: context.WorkspacePath, Stderr: &stderr},
"--output_base", context.BazelOutputBase, "build", "--discard_analysis_cache")
result, err := context.BazelCmd.Execute(
BazelCmdConfig{Dir: context.WorkspacePath, Stderr: &stderr},
"--output_base", context.BazelOutputBase, "build", "--discard_analysis_cache")

if result != 0 || err != nil {
return fmt.Errorf("failed to discard Bazel analysis cache in %v: %w. Stderr from Bazel ↓↓\n%v", context.WorkspacePath, err, stderr.String())
if result != 0 || err != nil {
return fmt.Errorf("failed to discard Bazel analysis cache in %v: %w. Stderr from Bazel ↓↓\n%v", context.WorkspacePath, err, stderr.String())
}
}
}

// --discard_analysis_cache defers some of its cleanup to the start of the next build.
// Perform a no-op build to flush any in-build state from the previous one.
{
var stderr bytes.Buffer
// --discard_analysis_cache defers some of its cleanup to the start of the next build.
// Perform a no-op build to flush any in-build state from the previous one.
{
var stderr bytes.Buffer

result, err := context.BazelCmd.Execute(
BazelCmdConfig{Dir: context.WorkspacePath, Stderr: &stderr},
"--output_base", context.BazelOutputBase, "build")
result, err := context.BazelCmd.Execute(
BazelCmdConfig{Dir: context.WorkspacePath, Stderr: &stderr},
"--output_base", context.BazelOutputBase, "build")

if result != 0 || err != nil {
return fmt.Errorf("failed to run no-op build after discarding Bazel analysis cache in %v: %w. Stderr:\n%v",
context.WorkspacePath, err, stderr.String())
if result != 0 || err != nil {
return fmt.Errorf("failed to run no-op build after discarding Bazel analysis cache in %v: %w. Stderr:\n%v",
context.WorkspacePath, err, stderr.String())
}
}
return nil
} else {
return fmt.Errorf("unrecognized analysis cache discard strategy: %v", context.AnalysisCacheClearStrategy)
}
return nil
}

func BazelOutputBase(workingDirectory string, BazelCmd BazelCmd) (string, error) {
Expand Down

0 comments on commit 7d4f40c

Please sign in to comment.