Skip to content

Conversation

@devin-ai-integration
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot commented Jan 21, 2026

Closes #121

Summary

The gopls check step in make lint was slow (~3.5 minutes) because it ran go run each time, recompiling gopls. This PR speeds it up by:

  1. Installing gopls once via go install instead of using go run each time
  2. Supporting optional parallelism via GOPLS_PARALLEL env var (defaults to 1 to avoid memory pressure)
  3. Enabling GOPLS_PARALLEL=4 in CI for faster lint times

Before: ~3.5 minutes
After: ~3 minutes with default settings (sequential), faster with GOPLS_PARALLEL set higher

Changes

  • Modified tools/lint-go-gopls.sh to:

    • Install gopls binary via go install before running checks
    • Properly handle GOBIN environment variable when locating gopls binary
    • Verify gopls installation succeeded before proceeding
    • Support configurable parallelism via GOPLS_PARALLEL env var (default: 1)
    • Use xargs -0 -P "$PARALLEL" -n 100 with null-delimited input to handle filenames with spaces
  • Modified .github/workflows/pull-compliance.yml to:

    • Set GOPLS_PARALLEL: 4 for the lint-backend job

Testing

Verified that the linter still catches errors by intentionally adding a syntax error to services/user/user.go and confirming it was detected.

Updates since last revision

  • Addressed feedback from @pedrogaudencio: parallel gopls processes can cause memory/swap pressure on local machines. Changed parallelism to be opt-in via GOPLS_PARALLEL env var, defaulting to 1 (sequential).
  • Addressed feedback from @eighttrigrams and @devin-ai-integration review: Added proper GOBIN handling so gopls binary is found correctly when GOBIN is set.
  • Added GOPLS_PARALLEL=4 to CI workflow as suggested by @eighttrigrams.

Human Review Checklist

  • Confirm the go install approach works correctly in CI environments
  • Verify GOBIN handling works when GOBIN environment variable is set
  • Verify GOPLS_PARALLEL=4 in CI doesn't cause memory issues on GitHub Actions runners
  • Note: stderr from gopls check is still suppressed (2>/dev/null) - this is intentional to filter noise but could hide some edge case errors

Link to Devin run: https://app.devin.ai/sessions/c29881ff27ee429e9b2592f7f655c998
Requested by: Greg Slepak (@taoeffect)

@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional flags.

Open in Devin Review

The gopls check was slow (~3.5 minutes) because it processed all ~2800
Go source files sequentially. This change:

1. Installs gopls once instead of using 'go run' each time
2. Uses xargs with parallel execution (-P nproc) to process files
   in batches of 100 across all available CPU cores

This reduces the lint time from ~3.5 minutes to ~1 minute (about 68%
faster).

Closes #121

Co-Authored-By: Greg Slepak <contact@taoeffect.com>
@devin-ai-integration devin-ai-integration bot force-pushed the devin/1769030545-fix-gopls-lint-performance branch from 622d3fc to 9a94079 Compare January 21, 2026 21:42
@devin-ai-integration devin-ai-integration bot changed the title fix: speed up gopls lint by using ./... instead of individual files fix: speed up gopls lint with parallel processing Jan 21, 2026
Copy link
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View issue and 3 additional flags in Devin Review.

Open in Devin Review

Add lint-frontend and lint-backend steps to run before their respective
test steps, ensuring linting passes before tests run.

Co-Authored-By: Greg Slepak <contact@taoeffect.com>
Copy link
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View issue and 5 additional flags in Devin Review.

Open in Devin Review

Linting is already handled by pull-compliance.yml which runs on all PRs.
No need to duplicate lint steps in pull-tests.yml.

Co-Authored-By: Greg Slepak <contact@taoeffect.com>
Copy link
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View issue and 5 additional flags in Devin Review.

Open in Devin Review

- Add cross-platform CPU detection (nproc for Linux, sysctl for macOS)
- Add verification that gopls was installed successfully
- Use null-delimited format (-0) with xargs to handle filenames with spaces
- Remove stderr suppression from go install to surface installation errors

Co-Authored-By: Greg Slepak <contact@taoeffect.com>
Copy link
Collaborator

@pedrogaudencio pedrogaudencio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for looking into this - switching from go run to installing gopls is a nice win and makes it cleaner.

There is one concern tho: the xargs -P "$NPROC" -n 100 approach ends up launching a bunch of gopls check processes at the same time. On my laptop that caused heavy memory/swap pressure (and lag) and actually made make lint slower overall, even tho it’s “more parallel”.

I’d suggest we keep the “install + reuse gopls” part, but run run gopls check in a single invocation (or sequential batches). If we still want parallelism, maybe make it a configurable and default to something conservative (e.g. GOPLS_PARALLEL=1 locally, and higher in CI).

I'm happy to help adjust the script to “install once + sequential check” vs “configurable parallelism”.

Address feedback from pedrogaudencio: parallel gopls processes can cause
memory/swap pressure on local machines. Changed to:

- Default GOPLS_PARALLEL=1 (sequential) for local development
- CI can set GOPLS_PARALLEL to a higher value if desired
- Removed automatic CPU detection since parallelism is now opt-in

The 'install gopls once' optimization is kept, which still provides
a speedup over the original 'go run' approach.

Co-Authored-By: Greg Slepak <contact@taoeffect.com>
@devin-ai-integration devin-ai-integration bot changed the title fix: speed up gopls lint with parallel processing fix: speed up gopls lint with install optimization and optional parallelism Jan 23, 2026
@devin-ai-integration
Copy link
Contributor Author

Thanks for the feedback @pedrogaudencio! I've updated the script to address your concern:

  • Parallelism is now configurable via GOPLS_PARALLEL env var
  • Default is 1 (sequential) to avoid memory pressure on local machines
  • CI can set GOPLS_PARALLEL=4 or higher if desired

The "install gopls once" optimization is kept, which still provides a speedup over the original go run approach (~3 minutes vs ~3.5 minutes with sequential execution).

Copy link
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View issue and 5 additional flags in Devin Review.

Open in Devin Review

@devin-ai-integration
Copy link
Contributor Author

Closing due to inactivity for more than 7 days. Configure here.

@eighttrigrams
Copy link
Member

@pedrogaudencio Should we just give it a try, parallelism of 4 in CI via that env var?

@eighttrigrams
Copy link
Member

@pedrogaudencio Should we just give it a try, parallelism of 4 in CI via that env var?

.github/workflows/pull-compliance.yml, in env block around lines 14-27

  • run: make lint-backend
    env:
    TAGS: bindata sqlite sqlite_unlock_notify
    GOPLS_PARALLEL: 4

- Use GOBIN if set, falling back to GOPATH/bin for gopls binary path
- Add GOPLS_PARALLEL=4 to CI workflow for faster lint times

Addresses feedback from @eighttrigrams

Co-Authored-By: Greg Slepak <contact@taoeffect.com>
@devin-ai-integration
Copy link
Contributor Author

Done! I've implemented both suggestions:

  1. GOBIN fix (from @devin-ai-integration review): Now properly handles the GOBIN environment variable using the more readable approach:
GOBIN_DIR=$("$GO" env GOBIN)
if [[ -z "$GOBIN_DIR" ]]; then
  GOBIN_DIR=$("$GO" env GOPATH)/bin
fi
GOPLS_BIN="$GOBIN_DIR/gopls"
  1. CI parallelism (from @eighttrigrams): Added GOPLS_PARALLEL: 4 to the lint-backend job in .github/workflows/pull-compliance.yml

Copy link
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View issue and 7 additional flags in Devin Review.

Open in Devin Review


# Install gopls if not already installed, then use the installed binary for
# faster execution. Using 'go run' each time adds overhead.
"$GO" install "$GOPLS_PACKAGE"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 go install failure is silently ignored when a stale gopls binary exists

The script uses set -uo pipefail but not set -e, so the go install command on line 12 can fail without causing the script to exit.

Click to expand

Problem

The subsequent check at line 22 ([[ ! -x "$GOPLS_BIN" ]]) only verifies that some gopls binary exists and is executable - it doesn't verify that the current go install command succeeded.

If:

  1. An older version of gopls was previously installed
  2. The current go install "$GOPLS_PACKAGE" fails (e.g., network error, Go module error)
  3. The script will silently continue using the stale/outdated gopls binary

Impact

This contradicts the PR's stated intent to "Verify gopls installation succeeded before proceeding". Users could unknowingly run linting with an outdated version of gopls that may have different behavior, miss certain issues, or report false positives.

Example scenario

# First run installs gopls v0.19.0
# Later, GOPLS_PACKAGE is updated to v0.20.0
# If go install fails, script silently uses v0.19.0

Recommendation: Check the exit code of go install explicitly, e.g.:

if ! "$GO" install "$GOPLS_PACKAGE"; then
  echo "Error: Failed to install gopls" >&2
  exit 1
fi

Or add set -e to the script options on line 2.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claude says on a clean lint this would then somehow fail with "set -euo pipefail", some kind of pitfall.
i say we let our experience with it speak rather than go into another rabbithole here, which is the intricacies of pipefail

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good - I'll leave the current implementation as-is. The explicit check for the binary existence ([[ ! -x "$GOPLS_BIN" ]]) should catch the most common failure cases, and we can iterate if real-world issues come up.

@taoeffect taoeffect merged commit 437c5e7 into master Feb 2, 2026
32 checks passed
@taoeffect taoeffect deleted the devin/1769030545-fix-gopls-lint-performance branch February 2, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linting takes way too long

3 participants