github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/events/event_manager.go (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package events 18 19 import ( 20 "context" 21 "strconv" 22 23 "github.com/kaleido-io/firefly/internal/broadcast" 24 "github.com/kaleido-io/firefly/internal/config" 25 "github.com/kaleido-io/firefly/internal/data" 26 "github.com/kaleido-io/firefly/internal/i18n" 27 "github.com/kaleido-io/firefly/internal/log" 28 "github.com/kaleido-io/firefly/internal/privatemessaging" 29 "github.com/kaleido-io/firefly/internal/retry" 30 "github.com/kaleido-io/firefly/pkg/blockchain" 31 "github.com/kaleido-io/firefly/pkg/database" 32 "github.com/kaleido-io/firefly/pkg/dataexchange" 33 "github.com/kaleido-io/firefly/pkg/fftypes" 34 "github.com/kaleido-io/firefly/pkg/identity" 35 "github.com/kaleido-io/firefly/pkg/publicstorage" 36 ) 37 38 type EventManager interface { 39 NewPins() chan<- int64 40 NewEvents() chan<- int64 41 NewSubscriptions() chan<- *fftypes.UUID 42 DeletedSubscriptions() chan<- *fftypes.UUID 43 DeleteDurableSubscription(ctx context.Context, subDef *fftypes.Subscription) (err error) 44 CreateDurableSubscription(ctx context.Context, subDef *fftypes.Subscription) (err error) 45 Start() error 46 WaitStop() 47 48 // Bound blockchain callbacks 49 TxSubmissionUpdate(bi blockchain.Plugin, txTrackingID string, txState blockchain.TransactionStatus, protocolTxID, errorMessage string, additionalInfo fftypes.JSONObject) error 50 BatchPinComplete(bi blockchain.Plugin, batch *blockchain.BatchPin, signingIdentity string, protocolTxID string, additionalInfo fftypes.JSONObject) error 51 52 // Bound dataexchange callbacks 53 TransferResult(dx dataexchange.Plugin, trackingID string, status fftypes.OpStatus, info string, additionalInfo fftypes.JSONObject) 54 BLOBReceived(dx dataexchange.Plugin, peerID string, ns string, id fftypes.UUID) 55 MessageReceived(dx dataexchange.Plugin, peerID string, data []byte) 56 } 57 58 type eventManager struct { 59 ctx context.Context 60 publicstorage publicstorage.Plugin 61 database database.Plugin 62 identity identity.Plugin 63 broadcast broadcast.Manager 64 messaging privatemessaging.Manager 65 data data.Manager 66 subManager *subscriptionManager 67 retry retry.Retry 68 aggregator *aggregator 69 newEventNotifier *eventNotifier 70 newPinNotifier *eventNotifier 71 opCorrelationRetries int 72 defaultTransport string 73 } 74 75 func NewEventManager(ctx context.Context, pi publicstorage.Plugin, di database.Plugin, ii identity.Plugin, bm broadcast.Manager, pm privatemessaging.Manager, dm data.Manager) (EventManager, error) { 76 if pi == nil || di == nil || ii == nil || dm == nil { 77 return nil, i18n.NewError(ctx, i18n.MsgInitializationNilDepError) 78 } 79 newPinNotifier := newEventNotifier(ctx, "pins") 80 newEventNotifier := newEventNotifier(ctx, "events") 81 em := &eventManager{ 82 ctx: log.WithLogField(ctx, "role", "event-manager"), 83 publicstorage: pi, 84 database: di, 85 identity: ii, 86 broadcast: bm, 87 messaging: pm, 88 data: dm, 89 retry: retry.Retry{ 90 InitialDelay: config.GetDuration(config.EventAggregatorRetryInitDelay), 91 MaximumDelay: config.GetDuration(config.EventAggregatorRetryMaxDelay), 92 Factor: config.GetFloat64(config.EventAggregatorRetryFactor), 93 }, 94 defaultTransport: config.GetString(config.EventTransportsDefault), 95 opCorrelationRetries: config.GetInt(config.EventAggregatorOpCorrelationRetries), 96 newEventNotifier: newEventNotifier, 97 newPinNotifier: newPinNotifier, 98 aggregator: newAggregator(ctx, di, bm, pm, dm, newPinNotifier), 99 } 100 101 var err error 102 if em.subManager, err = newSubscriptionManager(ctx, di, newEventNotifier); err != nil { 103 return nil, err 104 } 105 106 return em, nil 107 } 108 109 func (em *eventManager) Start() (err error) { 110 err = em.subManager.start() 111 if err == nil { 112 err = em.aggregator.start() 113 } 114 return err 115 } 116 117 func (em *eventManager) NewEvents() chan<- int64 { 118 return em.newEventNotifier.newEvents 119 } 120 121 func (em *eventManager) NewPins() chan<- int64 { 122 return em.newPinNotifier.newEvents 123 } 124 125 func (em *eventManager) NewSubscriptions() chan<- *fftypes.UUID { 126 return em.subManager.newSubscriptions 127 } 128 129 func (em *eventManager) DeletedSubscriptions() chan<- *fftypes.UUID { 130 return em.subManager.deletedSubscriptions 131 } 132 133 func (em *eventManager) WaitStop() { 134 em.subManager.close() 135 <-em.aggregator.eventPoller.closed 136 } 137 138 func (em *eventManager) CreateDurableSubscription(ctx context.Context, subDef *fftypes.Subscription) (err error) { 139 if subDef.Namespace == "" || subDef.Name == "" || subDef.ID == nil { 140 return i18n.NewError(ctx, i18n.MsgInvalidSubscription) 141 } 142 143 if subDef.Transport == "" { 144 subDef.Transport = em.defaultTransport 145 } 146 // Check it can be parsed before inserting (the submanager will check again when processing the creation, so we discard the result) 147 if _, err = em.subManager.parseSubscriptionDef(ctx, subDef); err != nil { 148 return err 149 } 150 151 // Do a check first for existence, to give a nice 409 if we find one 152 existing, _ := em.database.GetSubscriptionByName(ctx, subDef.Namespace, subDef.Name) 153 if existing != nil { 154 return i18n.NewError(ctx, i18n.MsgAlreadyExists, "subscription", subDef.Namespace, subDef.Name) 155 } 156 157 // We lock in the starting sequence at creation time, rather than when the first dispatcher 158 // starts, as that's a more obvious behavior for users 159 sequence, err := calcFirstOffset(ctx, em.database, subDef.Options.FirstEvent) 160 if err != nil { 161 return err 162 } 163 lockedInFirstEvent := fftypes.SubOptsFirstEvent(strconv.FormatInt(sequence, 10)) 164 subDef.Options.FirstEvent = &lockedInFirstEvent 165 166 // The event in the database for the creation of the susbscription, will asynchronously update the submanager 167 return em.database.UpsertSubscription(ctx, subDef, false) 168 } 169 170 func (em *eventManager) DeleteDurableSubscription(ctx context.Context, subDef *fftypes.Subscription) (err error) { 171 // The event in the database for the deletion of the susbscription, will asynchronously update the submanager 172 return em.database.DeleteSubscriptionByID(ctx, subDef.ID) 173 }