Skip to content
Snippets Groups Projects
approvers.go 3.85 KiB
package tangle

import (
	"github.com/dgraph-io/badger"
	"github.com/iotaledger/goshimmer/packages/database"
	"github.com/iotaledger/goshimmer/packages/datastructure"
	"github.com/iotaledger/goshimmer/packages/errors"
	"github.com/iotaledger/goshimmer/packages/model/approvers"
	"github.com/iotaledger/goshimmer/packages/node"
	"github.com/iotaledger/goshimmer/packages/ternary"
)

// region global public api ////////////////////////////////////////////////////////////////////////////////////////////

// GetApprovers retrieves approvers from the database.
func GetApprovers(transactionHash ternary.Trytes, computeIfAbsent ...func(ternary.Trytes) *approvers.Approvers) (result *approvers.Approvers, err errors.IdentifiableError) {
	if cacheResult := approversCache.ComputeIfAbsent(transactionHash, func() interface{} {
		if dbApprovers, dbErr := getApproversFromDatabase(transactionHash); dbErr != nil {
			err = dbErr

			return nil
		} else if dbApprovers != nil {
			return dbApprovers
		} else {
			if len(computeIfAbsent) >= 1 {
				return computeIfAbsent[0](transactionHash)
			}

			return nil
		}
	}); cacheResult != nil && cacheResult.(*approvers.Approvers) != nil {
		result = cacheResult.(*approvers.Approvers)
	}

	return
}

func ContainsApprovers(transactionHash ternary.Trytes) (result bool, err errors.IdentifiableError) {
	if approversCache.Contains(transactionHash) {
		result = true
	} else {
		result, err = databaseContainsApprovers(transactionHash)
	}

	return
}

func StoreApprovers(approvers *approvers.Approvers) {
	approversCache.Set(approvers.GetHash(), approvers)
}

// region lru cache ////////////////////////////////////////////////////////////////////////////////////////////////////

var approversCache = datastructure.NewLRUCache(APPROVERS_CACHE_SIZE, &datastructure.LRUCacheOptions{
	EvictionCallback: onEvictApprovers,
})

func onEvictApprovers(_ interface{}, value interface{}) {
	if evictedApprovers := value.(*approvers.Approvers); evictedApprovers.GetModified() {
		go func(evictedApprovers *approvers.Approvers) {
			if err := storeApproversInDatabase(evictedApprovers); err != nil {
				panic(err)
			}
		}(evictedApprovers)
	}
}

const (
	APPROVERS_CACHE_SIZE = 50000
)

// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////

// region database /////////////////////////////////////////////////////////////////////////////////////////////////////

var approversDatabase database.Database

func configureApproversDatabase(plugin *node.Plugin) {
	if db, err := database.Get("approvers"); err != nil {
		panic(err)
	} else {
		approversDatabase = db
	}
}

func storeApproversInDatabase(approvers *approvers.Approvers) errors.IdentifiableError {
	if approvers.GetModified() {
		if err := transactionMetadataDatabase.Set(approvers.GetHash().CastToBytes(), approvers.Marshal()); err != nil {
			return ErrDatabaseError.Derive(err, "failed to store transaction metadata")
		}

		approvers.SetModified(false)
	}

	return nil
}

func getApproversFromDatabase(transactionHash ternary.Trytes) (*approvers.Approvers, errors.IdentifiableError) {
	approversData, err := approversDatabase.Get(transactionHash.CastToBytes())
	if err != nil {
		if err == badger.ErrKeyNotFound {
			return nil, nil
		}

		return nil, ErrDatabaseError.Derive(err, "failed to retrieve approvers")
	}

	var result approvers.Approvers
	if err = result.Unmarshal(approversData); err != nil {
		panic(err)
	}

	return &result, nil
}

func databaseContainsApprovers(transactionHash ternary.Trytes) (bool, errors.IdentifiableError) {
	if contains, err := approversDatabase.Contains(transactionHash.CastToBytes()); err != nil {
		return false, ErrDatabaseError.Derive(err, "failed to check if the approvers exists")
	} else {
		return contains, nil
	}
}

// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////