Skip to content

Commit

Permalink
renew image time to cleanup on Load
Browse files Browse the repository at this point in the history
That will delay the image cleanup after it's loaded
by preview, so the commenter would have more time
to post it before it will be removed.

I think it's not a universal solution because
the load of the image will happen only on the first
comment preview, and then the commenter will see
image from the browser cache and will never
request it again before posting the comment.
Based on that I think we'll give commenter more time
but only after the very first preview after image upload.
  • Loading branch information
paskal committed May 7, 2021
1 parent 4a2ae04 commit fd1c47a
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 7 deletions.
4 changes: 4 additions & 0 deletions backend/_example/memory_store/accessor/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ func (m *MemImage) Load(id string) ([]byte, error) {
img, ok := m.images[id]
if !ok {
img, ok = m.imagesStaging[id]
// update timestamp of image in staging in case image was found
if ok {
m.insertTime[id] = time.Now()
}
}
m.RUnlock()
if !ok {
Expand Down
16 changes: 15 additions & 1 deletion backend/_example/memory_store/server/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,21 @@ func TestRPC_imgCleanupHndl(t *testing.T) {
assert.Equal(t, 1462, len(img))
assert.Equal(t, gopherPNGBytes(), img)

// cleanup
// wait for image to expire
time.Sleep(time.Millisecond * 50)
// load to renew the time to cleanup
_, err = ri.Load(id)
assert.NoError(t, err)

// cleanup, should not affect the new image
err = ri.Cleanup(context.TODO(), time.Millisecond*45)
assert.NoError(t, err)

// load after cleanup should succeed
_, err = ri.Load(id)
assert.NoError(t, err, "image is still on staging because it's cleanup timer was reset on load")

// cleanup with short TTL, should remove the image from staging
err = ri.Cleanup(context.TODO(), time.Nanosecond)
assert.NoError(t, err)

Expand Down
16 changes: 16 additions & 0 deletions backend/app/store/image/bolt_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,14 @@ func (b *Bolt) Commit(id string) error {
// Load image from DB
func (b *Bolt) Load(id string) ([]byte, error) {
var data []byte
var updateTimestamp bool
err := b.db.View(func(tx *bolt.Tx) error {
data = tx.Bucket([]byte(imagesBktName)).Get([]byte(id))
if data == nil {
data = tx.Bucket([]byte(imagesStagedBktName)).Get([]byte(id))
if data != nil {
updateTimestamp = true
}
}
if data == nil {
return errors.Errorf("can't load image %s", id)
Expand All @@ -101,6 +105,18 @@ func (b *Bolt) Load(id string) ([]byte, error) {
// separate error handler to return nil and not empty []byte
return nil, err
}
if updateTimestamp {
_ = b.db.Update(func(tx *bolt.Tx) error {
tsBuf := &bytes.Buffer{}
if err := binary.Write(tsBuf, binary.LittleEndian, time.Now().UnixNano()); err != nil {
return errors.Wrapf(err, "can't serialize timestamp for %s", id)
}
if err := tx.Bucket([]byte(insertTimeBktName)).Put([]byte(id), tsBuf.Bytes()); err != nil {
return errors.Wrapf(err, "can't put to bucket with %s", id)
}
return nil
})
}
return data, nil
}

Expand Down
7 changes: 5 additions & 2 deletions backend/app/store/image/bolt_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,13 @@ func TestBoltStore_Cleanup(t *testing.T) {
err = svc.Commit(img3)
require.NoError(t, err)

err = svc.Cleanup(context.Background(), time.Millisecond*10)
// load second image to reset it's time to cleanup
_, err = svc.Load(img2)
require.NoError(t, err)
err = svc.Cleanup(context.Background(), time.Millisecond*100)
assert.NoError(t, err)

assertBoltImgNil(t, svc.db, imagesStagedBktName, img2)
assertBoltImgNotNil(t, svc.db, imagesStagedBktName, img2)
assertBoltImgNil(t, svc.db, imagesBktName, img2)
assertBoltImgNotNil(t, svc.db, imagesBktName, img3)
assert.NoError(t, err)
Expand Down
5 changes: 5 additions & 0 deletions backend/app/store/image/fs_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ func (f *FileSystem) Load(id string) ([]byte, error) {
if err != nil {
file = f.location(f.Staging, id)
_, err = os.Stat(file)
// update timestamp of image in staging in case image was found
log.Printf("[ERROR] filename: %s, err: %s", file, err)
if err == nil {
_ = os.Chtimes(file, time.Now(), time.Now())
}
}
return file, errors.Wrapf(err, "can't get image stats for %s", id)
}
Expand Down
6 changes: 4 additions & 2 deletions backend/app/store/image/fs_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,16 @@ func TestFsStore_Cleanup(t *testing.T) {
_, err = os.Stat(img3)
assert.NoError(t, err, "file on staging")

time.Sleep(200 * time.Millisecond) // make all images expired
time.Sleep(200 * time.Millisecond) // make all images expired
_, err = svc.Load("user2/blah_ff3.png") // load third image to reset it's time to cleanup
assert.NoError(t, err)
err = svc.Cleanup(context.Background(), time.Millisecond*300)
assert.NoError(t, err)

_, err = os.Stat(img2)
assert.Error(t, err, "no file on staging anymore")
_, err = os.Stat(img3)
assert.Error(t, err, "no file on staging anymore")
assert.NoError(t, err, "third image is still on staging because it's cleanup timer was reset on load")
}

func TestFsStore_Info(t *testing.T) {
Expand Down
3 changes: 1 addition & 2 deletions backend/app/store/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"encoding/base64"
"fmt"
"image"

// support gif and jpeg images decoding
_ "image/gif"
_ "image/jpeg"
Expand Down Expand Up @@ -80,7 +79,7 @@ type StoreInfo struct {
type Store interface {
Info() (StoreInfo, error) // get meta information about storage
Save(id string, img []byte) error // store image with passed id to staging
Load(id string) ([]byte, error) // load image by ID
Load(id string) ([]byte, error) // load image by ID, if it is not yet committed then update it's expire date

Commit(id string) error // move image from staging to permanent
Cleanup(ctx context.Context, ttl time.Duration) error // run removal loop for old images on staging
Expand Down

0 comments on commit fd1c47a

Please sign in to comment.