storage: streamline ETag calculation

This commit introduces some helpers so that ETags can be calculated at
the same time that files get read or written. Besides looking nicer, it
should also help reduce lock contention around file access, as files do
not need to be opened twice anymore.
This commit is contained in:
Conrad Hoffmann 2024-11-07 17:50:09 +01:00
parent fe0a0d0d00
commit 665b206709
4 changed files with 89 additions and 54 deletions

View file

@ -68,21 +68,21 @@ func vcardPropFilter(card vcard.Card, props []string) vcard.Card {
return result
}
func vcardFromFile(path string, propFilter []string) (vcard.Card, error) {
func vcardAndEtagFromFile(path string, propFilter []string) (vcard.Card, string, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
return nil, "", err
}
defer f.Close()
dec := vcard.NewDecoder(f)
src := NewETagReader(f)
dec := vcard.NewDecoder(src)
card, err := dec.Decode()
if err != nil {
return nil, err
return nil, "", err
}
return vcardPropFilter(card, propFilter), nil
return vcardPropFilter(card, propFilter), src.ETag(), nil
}
func (b *filesystemBackend) loadAllAddressObjects(ctx context.Context, urlPath string, propFilter []string) ([]carddav.AddressObject, error) {
@ -104,16 +104,10 @@ func (b *filesystemBackend) loadAllAddressObjects(ctx context.Context, urlPath s
return nil
}
// TODO use single file read for data & etag
b.RLock(filename)
defer b.RUnlock(filename)
card, err := vcardFromFile(filename, propFilter)
if err != nil {
return err
}
etag, err := etagForFile(filename)
card, etag, err := vcardAndEtagFromFile(filename, propFilter)
if err != nil {
return err
}
@ -127,6 +121,7 @@ func (b *filesystemBackend) loadAllAddressObjects(ctx context.Context, urlPath s
ETag: etag,
Card: card,
}
log.Debug().Str("path", obj.Path).Str("etag", etag).Int64("size", info.Size()).Msg("address object loaded")
result = append(result, obj)
return nil
})
@ -335,17 +330,12 @@ func (b *filesystemBackend) GetAddressObject(ctx context.Context, objPath string
propFilter = req.Props
}
card, err := vcardFromFile(localPath, propFilter)
card, etag, err := vcardAndEtagFromFile(localPath, propFilter)
if err != nil {
log.Debug().Str("path", localPath).Err(err).Msg("error reading calendar")
return nil, err
}
etag, err := etagForFile(localPath)
if err != nil {
return nil, err
}
obj := carddav.AddressObject{
Path: objPath,
ModTime: info.ModTime(),
@ -353,6 +343,7 @@ func (b *filesystemBackend) GetAddressObject(ctx context.Context, objPath string
ETag: etag,
Card: card,
}
log.Debug().Str("path", objPath).Str("etag", etag).Int64("size", info.Size()).Msg("returning address object")
return &obj, nil
}
@ -435,15 +426,12 @@ func (b *filesystemBackend) PutAddressObject(ctx context.Context, objPath string
}
defer f.Close()
enc := vcard.NewEncoder(f)
out := NewETagWriter(f)
enc := vcard.NewEncoder(out)
if err := enc.Encode(card); err != nil {
return nil, err
}
etag, err := etagForFile(localPath)
if err != nil {
return nil, err
}
info, err := f.Stat()
if err != nil {
return nil, err
@ -453,9 +441,10 @@ func (b *filesystemBackend) PutAddressObject(ctx context.Context, objPath string
Path: objPath,
ModTime: info.ModTime(),
ContentLength: info.Size(),
ETag: etag,
ETag: out.ETag(),
Card: card,
}
log.Debug().Str("path", r.Path).Str("etag", r.ETag).Int64("size", info.Size()).Msg("address object updated")
return &r, nil
}