github.com/Finschia/finschia-sdk@v0.48.1/store/streaming/constructor.go (about)

     1  package streaming
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"sync"
     7  
     8  	"github.com/spf13/cast"
     9  
    10  	"github.com/Finschia/finschia-sdk/baseapp"
    11  	"github.com/Finschia/finschia-sdk/codec"
    12  	serverTypes "github.com/Finschia/finschia-sdk/server/types"
    13  	"github.com/Finschia/finschia-sdk/store/streaming/file"
    14  	"github.com/Finschia/finschia-sdk/store/types"
    15  )
    16  
    17  // ServiceConstructor is used to construct a streaming service
    18  type ServiceConstructor func(opts serverTypes.AppOptions, keys []types.StoreKey, marshaller codec.BinaryCodec) (baseapp.StreamingService, error)
    19  
    20  // ServiceType enum for specifying the type of StreamingService
    21  type ServiceType int
    22  
    23  const (
    24  	Unknown ServiceType = iota
    25  	File
    26  	// add more in the future
    27  )
    28  
    29  // ServiceTypeFromString returns the streaming.ServiceType corresponding to the provided name
    30  func ServiceTypeFromString(name string) ServiceType {
    31  	switch strings.ToLower(name) {
    32  	case "file", "f":
    33  		return File
    34  	default:
    35  		return Unknown
    36  	}
    37  }
    38  
    39  // String returns the string name of a streaming.ServiceType
    40  func (sst ServiceType) String() string {
    41  	switch sst {
    42  	case File:
    43  		return "file"
    44  	default:
    45  		return "unknown"
    46  	}
    47  }
    48  
    49  // ServiceConstructorLookupTable is a mapping of streaming.ServiceTypes to streaming.ServiceConstructors
    50  var ServiceConstructorLookupTable = map[ServiceType]ServiceConstructor{
    51  	File: NewFileStreamingService,
    52  }
    53  
    54  // NewServiceConstructor returns the streaming.ServiceConstructor corresponding to the provided name
    55  func NewServiceConstructor(name string) (ServiceConstructor, error) {
    56  	ssType := ServiceTypeFromString(name)
    57  	if ssType == Unknown {
    58  		return nil, fmt.Errorf("unrecognized streaming service name %s", name)
    59  	}
    60  	if constructor, ok := ServiceConstructorLookupTable[ssType]; ok && constructor != nil {
    61  		return constructor, nil
    62  	}
    63  	return nil, fmt.Errorf("streaming service constructor of type %s not found", ssType.String())
    64  }
    65  
    66  // NewFileStreamingService is the streaming.ServiceConstructor function for creating a FileStreamingService
    67  func NewFileStreamingService(opts serverTypes.AppOptions, keys []types.StoreKey, marshaller codec.BinaryCodec) (baseapp.StreamingService, error) {
    68  	filePrefix := cast.ToString(opts.Get("streamers.file.prefix"))
    69  	fileDir := cast.ToString(opts.Get("streamers.file.write_dir"))
    70  	return file.NewStreamingService(fileDir, filePrefix, keys, marshaller)
    71  }
    72  
    73  // LoadStreamingServices is a function for loading StreamingServices onto the BaseApp using the provided AppOptions, codec, and keys
    74  // It returns the WaitGroup and quit channel used to synchronize with the streaming services and any error that occurs during the setup
    75  func LoadStreamingServices(bApp *baseapp.BaseApp, appOpts serverTypes.AppOptions, appCodec codec.BinaryCodec, keys map[string]*types.KVStoreKey) ([]baseapp.StreamingService, *sync.WaitGroup, error) {
    76  	// waitgroup and quit channel for optional shutdown coordination of the streaming service(s)
    77  	wg := new(sync.WaitGroup)
    78  	// configure state listening capabilities using AppOptions
    79  	streamers := cast.ToStringSlice(appOpts.Get("store.streamers"))
    80  	activeStreamers := make([]baseapp.StreamingService, 0, len(streamers))
    81  	for _, streamerName := range streamers {
    82  		// get the store keys allowed to be exposed for this streaming service
    83  		exposeKeyStrs := cast.ToStringSlice(appOpts.Get(fmt.Sprintf("streamers.%s.keys", streamerName)))
    84  		var exposeStoreKeys []types.StoreKey
    85  		if exposeAll(exposeKeyStrs) { // if list contains `*`, expose all StoreKeys
    86  			exposeStoreKeys = make([]types.StoreKey, 0, len(keys))
    87  			for _, storeKey := range keys {
    88  				exposeStoreKeys = append(exposeStoreKeys, storeKey)
    89  			}
    90  		} else {
    91  			exposeStoreKeys = make([]types.StoreKey, 0, len(exposeKeyStrs))
    92  			for _, keyStr := range exposeKeyStrs {
    93  				if storeKey, ok := keys[keyStr]; ok {
    94  					exposeStoreKeys = append(exposeStoreKeys, storeKey)
    95  				}
    96  			}
    97  		}
    98  		if len(exposeStoreKeys) == 0 { // short circuit if we are not exposing anything
    99  			continue
   100  		}
   101  		// get the constructor for this streamer name
   102  		constructor, err := NewServiceConstructor(streamerName)
   103  		if err != nil {
   104  			// close any services we may have already spun up before hitting the error on this one
   105  			for _, activeStreamer := range activeStreamers {
   106  				activeStreamer.Close()
   107  			}
   108  			return nil, nil, err
   109  		}
   110  		// generate the streaming service using the constructor, appOptions, and the StoreKeys we want to expose
   111  		streamingService, err := constructor(appOpts, exposeStoreKeys, appCodec)
   112  		if err != nil {
   113  			// close any services we may have already spun up before hitting the error on this one
   114  			for _, activeStreamer := range activeStreamers {
   115  				activeStreamer.Close()
   116  			}
   117  			return nil, nil, err
   118  		}
   119  		// register the streaming service with the BaseApp
   120  		bApp.SetStreamingService(streamingService)
   121  		// kick off the background streaming service loop
   122  		streamingService.Stream(wg)
   123  		// add to the list of active streamers
   124  		activeStreamers = append(activeStreamers, streamingService)
   125  	}
   126  	// if there are no active streamers, activeStreamers is empty (len == 0) and the waitGroup is not waiting on anything
   127  	return activeStreamers, wg, nil
   128  }
   129  
   130  func exposeAll(list []string) bool {
   131  	for _, ele := range list {
   132  		if ele == "*" {
   133  			return true
   134  		}
   135  	}
   136  	return false
   137  }