Skip to content

Commit

Permalink
Add PLGNCleanCache function
Browse files Browse the repository at this point in the history
  • Loading branch information
olomix committed Oct 13, 2023
1 parent 1f044cc commit 9dd4eff
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 27 deletions.
95 changes: 74 additions & 21 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,44 +10,97 @@ import (
)

var globalDB *badger.DB
var dbM sync.Mutex
var dbCnt int
var dbCond = sync.NewCond(&sync.Mutex{})

func CleanCache() (err error) {
dbCond.L.Lock()
for dbCnt != 0 {
dbCond.Wait()
}
defer dbCond.L.Unlock()

db, err := openDB()
if err != nil {
return err
}
defer func() {
err2 := db.Close()
if err2 != nil {
if err == nil {
err = err2
} else {
slog.Error("failed to close db", "err", err2)
}
}
}()

return db.DropAll()
}

func getCacheDB() (*badger.DB, func(), error) {
dbM.Lock()
defer dbM.Unlock()
dbCond.L.Lock()
defer dbCond.L.Unlock()

err := maybeOpenDB()
if err != nil {
return nil, nil, err
}

// Close the DB only once per calling getCacheDB().
var once sync.Once

releaseDB := func() {
dbM.Lock()
defer dbM.Unlock()

dbCnt--
if dbCnt == 0 {
err := globalDB.Close()
if err != nil {
slog.Error("failed to close db", "err", err)
once.Do(func() {
dbCond.L.Lock()
defer dbCond.L.Unlock()

dbCnt--
if dbCnt == 0 {
err2 := globalDB.Close()
if err2 != nil {
slog.Error("failed to close db", "err", err2)
}
globalDB = nil
}
globalDB = nil
}

dbCond.Broadcast()
})
}

return globalDB, releaseDB, nil
}

// If globalDB is nil, open a new DB, assign it to globalDB.
// Also increment dbCnt.
// DANGER: This function is not thread-safe and should be called only when
// dbCond.L is locked. Use getCacheDB() instead.
func maybeOpenDB() error {
if globalDB != nil {
dbCnt++
return globalDB, releaseDB, nil
return nil
}

badgerPath, err := getBadgerPath()
db, err := openDB()
if err != nil {
return nil, nil, err
return err
}
opts := badger.DefaultOptions(badgerPath)
globalDB, err = badger.Open(opts)

globalDB = db
dbCnt = 1

return nil
}

func openDB() (*badger.DB, error) {
badgerPath, err := getBadgerPath()
if err != nil {
return nil, nil, err
return nil, err
}

dbCnt = 1
return globalDB, releaseDB, nil
opts := badger.DefaultOptions(badgerPath)

return badger.Open(opts)
}

func getBadgerPath() (string, error) {
Expand Down
94 changes: 88 additions & 6 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package c_polygonid

import (
"errors"
"sync"
"testing"
"time"

"github.com/dgraph-io/badger/v4"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -77,27 +79,107 @@ func TestGetCacheDB(t *testing.T) {
require.Equal(t, db1, db2)

func() {
dbM.Lock()
defer dbM.Unlock()
dbCond.L.Lock()
defer dbCond.L.Unlock()
require.Equal(t, 2, dbCnt)
require.NotNil(t, globalDB)
}()

close1()

func() {
dbM.Lock()
defer dbM.Unlock()
dbCond.L.Lock()
defer dbCond.L.Unlock()
require.Equal(t, 1, dbCnt)
require.NotNil(t, globalDB)
}()

close2()

func() {
dbM.Lock()
defer dbM.Unlock()
dbCond.L.Lock()
defer dbCond.L.Unlock()
require.Equal(t, 0, dbCnt)
require.Nil(t, globalDB)
}()
}

func get(db *badger.DB, key string) string {
var v string
err := db.View(func(txn *badger.Txn) error {
i, err := txn.Get([]byte(key))
if err != nil {
return err
}
return i.Value(func(val []byte) error {
v = string(val)
return nil
})
})
if errors.Is(err, badger.ErrKeyNotFound) {
return ""
}
if err != nil {
panic(err)
}
return v
}

func set(db *badger.DB, key string, value string) {
err := db.Update(func(txn *badger.Txn) error {
return txn.Set([]byte(key), []byte(value))
})
if err != nil {
panic(err)
}
}

func TestCleanCache(t *testing.T) {
db1, close1, err := getCacheDB()
require.NoError(t, err)

set(db1, "key1", "val1")

var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
err2 := CleanCache()
if err2 != nil {
t.Error(err2)
}
}()

time.Sleep(10 * time.Millisecond)

db2, close2, err := getCacheDB()
require.NoError(t, err)
require.Equal(t, "val1", get(db2, "key1"))

close1()
close2()
time.Sleep(10 * time.Millisecond)

db3, close3, err := getCacheDB()
require.NoError(t, err)
require.Equal(t, "", get(db3, "key1"))
close3()

wg.Wait()
}

func TestMultipleCleanup(t *testing.T) {
_, close1, err := getCacheDB()
require.NoError(t, err)

require.Equal(t, 1, dbCnt)
require.NotNil(t, globalDB)

close1()
close1()
dbCond.L.Lock()
defer dbCond.L.Unlock()

require.Equal(t, 0, dbCnt)
require.Nil(t, globalDB)
}
14 changes: 14 additions & 0 deletions cmd/polygonid/polygonid.go
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,20 @@ func PLGNFreeStatus(status *C.PLGNStatus) {
C.free(unsafe.Pointer(status))
}

//export PLGNCleanCache
func PLGNCleanCache(status **C.PLGNStatus) bool {
_, cancel := logAPITime()
defer cancel()

err := c_polygonid.CleanCache()
if err != nil {
maybeCreateStatus(status, C.PLGNSTATUSCODE_ERROR, err.Error())
return false
}

return true
}

// createEnvConfig returns empty config if input json is nil.
func createEnvConfig(cfgJson *C.char) (c_polygonid.EnvConfig, error) {
var cfg c_polygonid.EnvConfig
Expand Down

0 comments on commit 9dd4eff

Please sign in to comment.