fabric-samples/off_chain_data/application-go/store.go
Stanislav Jakuschevskij fd1a1fc38b
Add first batch of pull request rework
- update Application section in README
- remove param name in app.go
- add error checks in processor/block.go
- move vars from model to transact logic
- move newAsset to transact
- use ID for well-known initialisms
- move randomelement, randomnint and differentelement to transact
- remove AssertDefined
- blockTxIdsJoinedByComma: use standard library to join elements
- return nil, instead of []byte{}
- remove go routine in listen.go
- move cache to parser
- inline processor in listen.go
- move store to main package
- move util to main package
- fixed failing cache issue
- fixed staticcheck issues
- removed cache function, implemented caching in the structs and methods

Signed-off-by: Stanislav Jakuschevskij <stas@two-giants.com>
2025-02-24 13:14:48 +01:00

101 lines
2.6 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"math"
"os"
"strconv"
"strings"
)
var storeFile = envOrDefault("STORE_FILE", "store.log")
var simulatedFailureCount = getSimulatedFailureCount()
var transactionCount uint = 0 // Used only to simulate failures
// Apply writes for a given transaction to off-chain data store, ideally in a single operation for fault tolerance.
type writer = func(data ledgerUpdate) error
// Ledger update made by a specific transaction.
type ledgerUpdate struct {
BlockNumber uint64
TransactionID string
Writes []write
}
// Description of a ledger Write that can be applied to an off-chain data store.
type write struct {
// Channel whose ledger is being updated.
ChannelName string `json:"channelName"`
// Namespace within the ledger.
Namespace string `json:"namespace"`
// Key name within the ledger namespace.
Key string `json:"key"`
// Whether the key and associated value are being deleted.
IsDelete bool `json:"isDelete"`
// If `isDelete` is false, the Value written to the key; otherwise ignored.
Value string `json:"value"`
}
// Apply writes for a given transaction to off-chain data store, ideally in a single operation for fault tolerance.
// This implementation just writes to a file.
func applyWritesToOffChainStore(data ledgerUpdate) error {
funcName := "applyWritesToOffChainStore"
if err := simulateFailureIfRequired(); err != nil {
return fmt.Errorf("in %s: %w", funcName, err)
}
writes := []string{}
for _, write := range data.Writes {
marshaled, err := json.Marshal(write)
if err != nil {
return fmt.Errorf("in %s: %w", funcName, err)
}
writes = append(writes, string(marshaled))
}
f, err := os.OpenFile(storeFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("in %s: %w", funcName, err)
}
if _, err := f.Write([]byte(strings.Join(writes, "\n") + "\n")); err != nil {
f.Close()
return fmt.Errorf("in %s: %w", funcName, err)
}
if err := f.Close(); err != nil {
return fmt.Errorf("in %s: %w", funcName, err)
}
return nil
}
func simulateFailureIfRequired() error {
if simulatedFailureCount > 0 && transactionCount >= simulatedFailureCount {
transactionCount = 0
return errors.New("expected error: simulated write failure")
}
transactionCount += 1
return nil
}
func getSimulatedFailureCount() uint {
valueAsString := envOrDefault("SIMULATED_FAILURE_COUNT", "0")
valueAsFloat, err := strconv.ParseFloat(valueAsString, 64)
if err != nil {
panic(err)
}
result := math.Floor(valueAsFloat)
if valueAsFloat < 0 {
panic(fmt.Errorf("invalid SIMULATED_FAILURE_COUNT value: %s", valueAsString))
}
return uint(result)
}