Skip to content

Commit b38e936

Browse files
authored
[perf] Only compute print-dev-env if needed (#898)
## Summary Stacked on #897 This significantly improves `run` and `shell` performance (`run` goes from 500ms -> 150ms without telemetry) Almost all remaining time for run and shell involves the `exec.Command` so this is close to as fast as it will get. ## How was it tested? ```bash devbox add hello time devbox run which hello ```
1 parent 4d5d77a commit b38e936

File tree

7 files changed

+69
-29
lines changed

7 files changed

+69
-29
lines changed

internal/cuecfg/hash.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package cuecfg
2+
3+
import (
4+
"crypto/sha256"
5+
"encoding/hex"
6+
"os"
7+
)
8+
9+
func FileHash(path string) (string, error) {
10+
data, err := os.ReadFile(path)
11+
if err != nil && !os.IsNotExist(err) {
12+
return "", err
13+
}
14+
hash := sha256.Sum256(data)
15+
return hex.EncodeToString(hash[:]), nil
16+
}
17+
18+
func Hash(s any) (string, error) {
19+
json, err := MarshalJSON(s)
20+
if err != nil {
21+
return "", err
22+
}
23+
hash := sha256.Sum256(json)
24+
return hex.EncodeToString(hash[:]), nil
25+
}

internal/impl/config.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
package impl
55

66
import (
7-
"crypto/sha256"
8-
"encoding/hex"
97
"io"
108
"net/http"
119
"net/url"
@@ -69,12 +67,7 @@ func (c *Config) MergedPackages(w io.Writer) []string {
6967
}
7068

7169
func (c *Config) Hash() (string, error) {
72-
json, err := cuecfg.MarshalJSON(c)
73-
if err != nil {
74-
return "", err
75-
}
76-
hash := sha256.Sum256(json)
77-
return hex.EncodeToString(hash[:]), nil
70+
return cuecfg.Hash(c)
7871
}
7972

8073
func readConfig(path string) (*Config, error) {

internal/impl/devbox.go

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ package impl
66

77
import (
88
"context"
9-
"crypto/md5"
10-
"encoding/hex"
119
"fmt"
1210
"io"
1311
"os"
@@ -33,6 +31,7 @@ import (
3331
"go.jetpack.io/devbox/internal/debug"
3432
"go.jetpack.io/devbox/internal/fileutil"
3533
"go.jetpack.io/devbox/internal/initrec"
34+
"go.jetpack.io/devbox/internal/lockfile"
3635
"go.jetpack.io/devbox/internal/nix"
3736
"go.jetpack.io/devbox/internal/pkgslice"
3837
"go.jetpack.io/devbox/internal/planner"
@@ -796,9 +795,8 @@ func (d *Devbox) computeNixEnv(
796795
debug.Log("computed environment PATH is: %s", env["PATH"])
797796

798797
d.setCommonHelperEnvVars(env)
799-
addHashToEnv(env)
800798

801-
return env, nil
799+
return env, addHashToEnv(env)
802800
}
803801

804802
var nixEnvCache map[string]string
@@ -810,7 +808,18 @@ var nixEnvWithPrintDevEnvCache map[string]string
810808
func (d *Devbox) nixEnv(ctx context.Context) (map[string]string, error) {
811809
var err error
812810
if nixEnvCache == nil {
813-
nixEnvCache, err = d.computeNixEnv(ctx, false /*usePrintDevEnvCache*/)
811+
usePrintDevEnvCache := false
812+
813+
// If lockfile is up-to-date, we can use the print-dev-env cache.
814+
if lock, err := lockfile.Local(d); err != nil {
815+
return nil, err
816+
} else if upToDate, err := lock.IsUpToDate(); err != nil {
817+
return nil, err
818+
} else if upToDate {
819+
usePrintDevEnvCache = true
820+
}
821+
822+
nixEnvCache, err = d.computeNixEnv(ctx, usePrintDevEnvCache)
814823
}
815824
return nixEnvCache, err
816825
}
@@ -1075,11 +1084,11 @@ func (d *Devbox) NixBins(ctx context.Context) ([]string, error) {
10751084
return lo.Values(bins), nil
10761085
}
10771086

1078-
func addHashToEnv(env map[string]string) {
1079-
h := md5.New()
1080-
for k, v := range env {
1081-
h.Write([]byte(k))
1082-
h.Write([]byte(v))
1087+
func addHashToEnv(env map[string]string) error {
1088+
hash, err := cuecfg.Hash(env)
1089+
if err == nil {
1090+
env[devboxShellEnvHashVarName] = hash
1091+
10831092
}
1084-
env[devboxShellEnvHashVarName] = hex.EncodeToString(h.Sum(nil)[:])
1093+
return err
10851094
}

internal/impl/packages.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ func (d *Devbox) ensurePackagesAreInstalled(ctx context.Context, mode installMod
163163
return err
164164
}
165165

166+
// Force print-dev-env cache to be recomputed.
167+
if _, err = d.computeNixEnv(ctx, false /*use cache*/); err != nil {
168+
return err
169+
}
170+
166171
return lock.Update()
167172
}
168173

internal/lockfile/lockfile.go renamed to internal/lockfile/local.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ type localLockFile struct {
1818
project devboxProject
1919
ConfigHash string `json:"config_hash"`
2020
NixProfileManifestHash string `json:"nix_profile_manifest_hash"`
21+
NixPrintDevEnvHash string `json:"nix_print_dev_env_hash"`
2122
}
2223

2324
func (l *localLockFile) equals(other *localLockFile) bool {
2425
return l.ConfigHash == other.ConfigHash &&
25-
l.NixProfileManifestHash == other.NixProfileManifestHash
26+
l.NixProfileManifestHash == other.NixProfileManifestHash &&
27+
l.NixPrintDevEnvHash == other.NixPrintDevEnvHash
2628
}
2729

2830
func (l *localLockFile) IsUpToDate() (bool, error) {
@@ -71,10 +73,16 @@ func forProject(project devboxProject) (*localLockFile, error) {
7173
return nil, err
7274
}
7375

76+
printDevEnvCacheHash, err := nix.PrintDevEnvCacheHash(project.ProjectDir())
77+
if err != nil {
78+
return nil, err
79+
}
80+
7481
newLock := &localLockFile{
7582
project: project,
7683
ConfigHash: configHash,
7784
NixProfileManifestHash: nixHash,
85+
NixPrintDevEnvHash: printDevEnvCacheHash,
7886
}
7987

8088
return newLock, nil

internal/nix/nix.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/pkg/errors"
1616
"github.com/samber/lo"
17+
"go.jetpack.io/devbox/internal/cuecfg"
1718
"go.jetpack.io/devbox/internal/debug"
1819
)
1920

@@ -147,6 +148,12 @@ func PrintDevEnv(ctx context.Context, args *PrintDevEnvArgs) (*printDevEnvOut, e
147148
return &out, nil
148149
}
149150

151+
func PrintDevEnvCacheHash(profileDir string) (string, error) {
152+
return cuecfg.FileHash(
153+
filepath.Join(profileDir, ".devbox", ".nix-print-dev-env-cache"),
154+
)
155+
}
156+
150157
func savePrintDevEnvCache(path string, out printDevEnvOut) error {
151158
data, err := json.Marshal(out)
152159
if err != nil {

internal/nix/profile.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package nix
33
import (
44
"bufio"
55
"bytes"
6-
"crypto/sha256"
7-
"encoding/hex"
86
"encoding/json"
97
"fmt"
108
"io"
@@ -15,6 +13,7 @@ import (
1513
"strings"
1614

1715
"github.com/fatih/color"
16+
"go.jetpack.io/devbox/internal/cuecfg"
1817
"go.jetpack.io/devbox/internal/redact"
1918
)
2019

@@ -334,13 +333,7 @@ func readManifest(profilePath string) (manifest, error) {
334333
}
335334

336335
func ManifestHash(profileDir string) (string, error) {
337-
path := filepath.Join(profileDir, ProfilePath, "manifest.json")
338-
data, err := os.ReadFile(path)
339-
if err != nil && !os.IsNotExist(err) {
340-
return "", err
341-
}
342-
hash := sha256.Sum256(data)
343-
return hex.EncodeToString(hash[:]), nil
336+
return cuecfg.FileHash(filepath.Join(profileDir, ProfilePath, "manifest.json"))
344337
}
345338

346339
func nextPriority(profilePath string) string {

0 commit comments

Comments
 (0)