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 }