bundle.go 3.85 KiB
package tangle
import (
"github.com/iotaledger/goshimmer/packages/database"
"github.com/iotaledger/goshimmer/packages/datastructure"
"github.com/iotaledger/goshimmer/packages/errors"
"github.com/iotaledger/goshimmer/packages/model/bundle"
"github.com/iotaledger/goshimmer/packages/typeutils"
"github.com/iotaledger/hive.go/node"
"github.com/iotaledger/iota.go/trinary"
)
// region global public api ////////////////////////////////////////////////////////////////////////////////////////////
// GetBundle retrieves bundle from the database.
func GetBundle(headerTransactionHash trinary.Trytes, computeIfAbsent ...func(trinary.Trytes) (*bundle.Bundle, errors.IdentifiableError)) (result *bundle.Bundle, err errors.IdentifiableError) {
if cacheResult := bundleCache.ComputeIfAbsent(headerTransactionHash, func() interface{} {
if dbBundle, dbErr := getBundleFromDatabase(headerTransactionHash); dbErr != nil {
err = dbErr
return nil
} else if dbBundle != nil {
return dbBundle
} else {
if len(computeIfAbsent) >= 1 {
if computedBundle, computedErr := computeIfAbsent[0](headerTransactionHash); computedErr != nil {
err = computedErr
} else {
return computedBundle
}
}
return nil
}
}); cacheResult != nil && cacheResult.(*bundle.Bundle) != nil {
result = cacheResult.(*bundle.Bundle)
}
return
}
func ContainsBundle(headerTransactionHash trinary.Trytes) (result bool, err errors.IdentifiableError) {
if bundleCache.Contains(headerTransactionHash) {
result = true
} else {
result, err = databaseContainsBundle(headerTransactionHash)
}
return
}
func StoreBundle(bundle *bundle.Bundle) {
bundleCache.Set(bundle.GetHash(), bundle)
}
// region lru cache ////////////////////////////////////////////////////////////////////////////////////////////////////
var bundleCache = datastructure.NewLRUCache(BUNDLE_CACHE_SIZE, &datastructure.LRUCacheOptions{
EvictionCallback: onEvictBundle,
})
func onEvictBundle(_ interface{}, value interface{}) {
if evictedBundle := value.(*bundle.Bundle); evictedBundle.GetModified() {
go func(evictedBundle *bundle.Bundle) {
if err := storeBundleInDatabase(evictedBundle); err != nil {
panic(err)
}
}(evictedBundle)
}
}
const (
BUNDLE_CACHE_SIZE = 500
)
// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
// region database /////////////////////////////////////////////////////////////////////////////////////////////////////
var bundleDatabase database.Database
func configureBundleDatabase(plugin *node.Plugin) {
if db, err := database.Get("bundle"); err != nil {
panic(err)
} else {
bundleDatabase = db
}
}
func storeBundleInDatabase(bundle *bundle.Bundle) errors.IdentifiableError {
if bundle.GetModified() {
if err := bundleDatabase.Set(typeutils.StringToBytes(bundle.GetHash()), bundle.Marshal()); err != nil {
return ErrDatabaseError.Derive(err, "failed to store bundle")
}
bundle.SetModified(false)
}
return nil
}
func getBundleFromDatabase(transactionHash trinary.Trytes) (*bundle.Bundle, errors.IdentifiableError) {
bundleData, err := bundleDatabase.Get(typeutils.StringToBytes(transactionHash))
if err != nil {
if err == database.ErrKeyNotFound {
return nil, nil
}
return nil, ErrDatabaseError.Derive(err, "failed to retrieve bundle")
}
var result bundle.Bundle
if err = result.Unmarshal(bundleData); err != nil {
panic(err)
}
return &result, nil
}
func databaseContainsBundle(transactionHash trinary.Trytes) (bool, errors.IdentifiableError) {
if contains, err := bundleDatabase.Contains(typeutils.StringToBytes(transactionHash)); err != nil {
return false, ErrDatabaseError.Derive(err, "failed to check if the bundle exists")
} else {
return contains, nil
}
}
// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////