-
Notifications
You must be signed in to change notification settings - Fork 60
feat: cache SHA384 entries #298
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
base: main
Are you sure you want to change the base?
Changes from all commits
baf8c76
ea5d605
6afe7f7
4330200
7729d29
83b1006
a5073e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,8 @@ import ( | |
| "os" | ||
| "path/filepath" | ||
| "time" | ||
|
|
||
| "golang.org/x/crypto/sha3" | ||
| ) | ||
|
|
||
| func DefaultDir(suffix string) string { | ||
|
|
@@ -89,41 +91,64 @@ func (cw *Writer) Digest() string { | |
| return cw.digest | ||
| } | ||
|
|
||
| const digestKind = "sha256" | ||
| // hashAlgo identifies a supported hash algorithm. | ||
| type hashAlgo string | ||
|
|
||
| const ( | ||
| SHA256 hashAlgo = "sha256" | ||
| SHA384 hashAlgo = "sha384" | ||
| ) | ||
|
upils marked this conversation as resolved.
|
||
|
|
||
| var hashAlgos = []hashAlgo{SHA256, SHA384} | ||
|
|
||
| func newHash(algo hashAlgo) (hash.Hash, error) { | ||
| switch algo { | ||
| case SHA256: | ||
| return sha256.New(), nil | ||
| case SHA384: | ||
| return sha3.New384(), nil | ||
| default: | ||
| return nil, fmt.Errorf("unsupported hash algorithm: %q", algo) | ||
| } | ||
| } | ||
|
|
||
| var ErrMiss = fmt.Errorf("not cached") | ||
|
|
||
| func (c *Cache) filePath(digest string) string { | ||
| return filepath.Join(c.Dir, digestKind, digest) | ||
| func (c *Cache) filePath(algo hashAlgo, digest string) string { | ||
| return filepath.Join(c.Dir, string(algo), digest) | ||
| } | ||
|
upils marked this conversation as resolved.
|
||
|
|
||
| func (c *Cache) Create(digest string) *Writer { | ||
| func (c *Cache) Create(algo hashAlgo, digest string) *Writer { | ||
| if c.Dir == "" { | ||
| return &Writer{err: fmt.Errorf("internal error: cache directory is unset")} | ||
| } | ||
| err := os.MkdirAll(filepath.Join(c.Dir, digestKind), 0755) | ||
| h, err := newHash(algo) | ||
| if err != nil { | ||
| return &Writer{err: fmt.Errorf("internal error: %v", err)} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function got a call with a piece of data that is coming from a place it has no idea, and then it also got an error that it has no idea about, because it has not inspected it, from a different function. How does it know it's an internal error? |
||
| } | ||
| err = os.MkdirAll(filepath.Join(c.Dir, string(algo)), 0755) | ||
| if err != nil { | ||
| return &Writer{err: fmt.Errorf("cannot create cache directory: %v", err)} | ||
| } | ||
| var file *os.File | ||
| if digest == "" { | ||
| file, err = os.CreateTemp(c.filePath(""), "tmp.*") | ||
| file, err = os.CreateTemp(filepath.Join(c.Dir, string(algo)), "tmp.*") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the pattern from the existing code not working? |
||
| } else { | ||
| file, err = os.Create(c.filePath(digest + ".tmp")) | ||
| file, err = os.Create(c.filePath(algo, digest+".tmp")) | ||
| } | ||
| if err != nil { | ||
| return &Writer{err: fmt.Errorf("cannot create cache file: %v", err)} | ||
| } | ||
| return &Writer{ | ||
| dir: c.Dir, | ||
| digest: digest, | ||
| hash: sha256.New(), | ||
| hash: h, | ||
| file: file, | ||
| } | ||
| } | ||
|
|
||
| func (c *Cache) Write(digest string, data []byte) error { | ||
| f := c.Create(digest) | ||
| func (c *Cache) Write(algo hashAlgo, digest string, data []byte) error { | ||
| f := c.Create(algo, digest) | ||
| _, err1 := f.Write(data) | ||
| err2 := f.Close() | ||
| if err1 != nil { | ||
|
|
@@ -132,11 +157,11 @@ func (c *Cache) Write(digest string, data []byte) error { | |
| return err2 | ||
| } | ||
|
|
||
| func (c *Cache) Open(digest string) (io.ReadSeekCloser, error) { | ||
| func (c *Cache) Open(algo hashAlgo, digest string) (io.ReadSeekCloser, error) { | ||
| if c.Dir == "" || digest == "" { | ||
| return nil, ErrMiss | ||
| } | ||
| filePath := c.filePath(digest) | ||
| filePath := c.filePath(algo, digest) | ||
|
upils marked this conversation as resolved.
|
||
| file, err := os.Open(filePath) | ||
| if os.IsNotExist(err) { | ||
| return nil, ErrMiss | ||
|
|
@@ -151,8 +176,8 @@ func (c *Cache) Open(digest string) (io.ReadSeekCloser, error) { | |
| return file, nil | ||
| } | ||
|
|
||
| func (c *Cache) Read(digest string) ([]byte, error) { | ||
| file, err := c.Open(digest) | ||
| func (c *Cache) Read(algo hashAlgo, digest string) ([]byte, error) { | ||
| file, err := c.Open(algo, digest) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
@@ -165,22 +190,28 @@ func (c *Cache) Read(digest string) ([]byte, error) { | |
| } | ||
|
|
||
| func (c *Cache) Expire(timeout time.Duration) error { | ||
| entries, err := os.ReadDir(filepath.Join(c.Dir, digestKind)) | ||
| if err != nil { | ||
| return fmt.Errorf("cannot list cache directory: %v", err) | ||
| } | ||
| expired := time.Now().Add(-timeout) | ||
| for _, entry := range entries { | ||
| finfo, err := entry.Info() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if finfo.ModTime().After(expired) { | ||
| for _, algo := range hashAlgos { | ||
| algoDir := filepath.Join(c.Dir, string(algo)) | ||
| entries, err := os.ReadDir(algoDir) | ||
|
upils marked this conversation as resolved.
|
||
| if os.IsNotExist(err) { | ||
| continue | ||
| } | ||
|
upils marked this conversation as resolved.
|
||
| err = os.Remove(filepath.Join(c.Dir, digestKind, finfo.Name())) | ||
| if err != nil { | ||
| return fmt.Errorf("cannot expire cache entry: %v", err) | ||
| return fmt.Errorf("cannot list cache directory: %v", err) | ||
| } | ||
| for _, entry := range entries { | ||
| finfo, err := entry.Info() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if finfo.ModTime().After(expired) { | ||
| continue | ||
| } | ||
| err = os.Remove(filepath.Join(algoDir, finfo.Name())) | ||
| if err != nil { | ||
| return fmt.Errorf("cannot expire cache entry: %v", err) | ||
| } | ||
| } | ||
| } | ||
| return nil | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public values/functions/etc with private types is an anti pattern.
Also, this already had a name, right above: digestKind. Note how the entire public API below talks about a "digest", not a "hash". The hash is the internal Go types and values used to construct them locally. The overall PR needs to be fixed to make the terminology clear and less conflicting.
Note that if you want to change everything to be "hash", I'm fine with that too. We just cannot sit in the middle and use both terms with no clear lines. If you do that, please don't use "algo" as there are better terms that are naturally short.