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/transactionmetadata" "github.com/iotaledger/goshimmer/packages/node" "github.com/iotaledger/goshimmer/packages/ternary" "github.com/iotaledger/goshimmer/packages/typeutils" ) // region public api /////////////////////////////////////////////////////////////////////////////////////////////////// func GetTransactionMetadata(transactionHash ternary.Trytes, computeIfAbsent ...func(ternary.Trytes) *transactionmetadata.TransactionMetadata) (result *transactionmetadata.TransactionMetadata, err errors.IdentifiableError) { if cacheResult := transactionMetadataCache.ComputeIfAbsent(transactionHash, func() interface{} { if transactionMetadata, dbErr := getTransactionMetadataFromDatabase(transactionHash); dbErr != nil { err = dbErr return nil } else if transactionMetadata != nil { return transactionMetadata } else { if len(computeIfAbsent) >= 1 { return computeIfAbsent[0](transactionHash) } return nil } }); !typeutils.IsInterfaceNil(cacheResult) { result = cacheResult.(*transactionmetadata.TransactionMetadata) } return } func ContainsTransactionMetadata(transactionHash ternary.Trytes) (result bool, err errors.IdentifiableError) { if transactionMetadataCache.Contains(transactionHash) { result = true } else { result, err = databaseContainsTransactionMetadata(transactionHash) } return } func StoreTransactionMetadata(transactionMetadata *transactionmetadata.TransactionMetadata) { transactionMetadataCache.Set(transactionMetadata.GetHash(), transactionMetadata) } // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// // region lru cache //////////////////////////////////////////////////////////////////////////////////////////////////// var transactionMetadataCache = datastructure.NewLRUCache(TRANSACTION_METADATA_CACHE_SIZE, &datastructure.LRUCacheOptions{ EvictionCallback: onEvictTransactionMetadata, }) func onEvictTransactionMetadata(_ interface{}, value interface{}) { if evictedTransactionMetadata := value.(*transactionmetadata.TransactionMetadata); evictedTransactionMetadata.GetModified() { go func(evictedTransactionMetadata *transactionmetadata.TransactionMetadata) { if err := storeTransactionMetadataInDatabase(evictedTransactionMetadata); err != nil { panic(err) } }(evictedTransactionMetadata) } } const ( TRANSACTION_METADATA_CACHE_SIZE = 50000 ) // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// // region database ///////////////////////////////////////////////////////////////////////////////////////////////////// var transactionMetadataDatabase database.Database func configureTransactionMetaDataDatabase(plugin *node.Plugin) { if db, err := database.Get("transactionMetadata"); err != nil { panic(err) } else { transactionMetadataDatabase = db } } func storeTransactionMetadataInDatabase(metadata *transactionmetadata.TransactionMetadata) errors.IdentifiableError { if metadata.GetModified() { if marshaledMetadata, err := metadata.Marshal(); err != nil { return err } else { if err := transactionMetadataDatabase.Set(metadata.GetHash().CastToBytes(), marshaledMetadata); err != nil { return ErrDatabaseError.Derive(err, "failed to store transaction metadata") } metadata.SetModified(false) } } return nil } func getTransactionMetadataFromDatabase(transactionHash ternary.Trytes) (*transactionmetadata.TransactionMetadata, errors.IdentifiableError) { txMetadata, err := transactionMetadataDatabase.Get(transactionHash.CastToBytes()) if err != nil { if err == badger.ErrKeyNotFound { return nil, nil } else { return nil, ErrDatabaseError.Derive(err, "failed to retrieve transaction") } } var result transactionmetadata.TransactionMetadata if err := result.Unmarshal(txMetadata); err != nil { panic(err) } return &result, nil } func databaseContainsTransactionMetadata(transactionHash ternary.Trytes) (bool, errors.IdentifiableError) { if contains, err := transactionMetadataDatabase.Contains(transactionHash.CastToBytes()); err != nil { return contains, ErrDatabaseError.Derive(err, "failed to check if the transaction metadata exists") } else { return contains, nil } } // endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////