code.vegaprotocol.io/vega@v0.79.0/datanode/candlesv2/service.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package candlesv2
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"sync"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/datanode/entities"
    25  	"code.vegaprotocol.io/vega/logging"
    26  )
    27  
    28  // CandleStore ...
    29  //
    30  //go:generate go run github.com/golang/mock/mockgen -destination mocks/candle_store_mock.go -package mocks code.vegaprotocol.io/vega/datanode/candlesv2 CandleStore
    31  type CandleStore interface {
    32  	GetCandleDataForTimeSpan(ctx context.Context, candleID string, from *time.Time, to *time.Time,
    33  		p entities.CursorPagination) ([]entities.Candle, entities.PageInfo, error)
    34  	GetCandlesForMarket(ctx context.Context, market string) (map[string]string, error)
    35  	CandleExists(ctx context.Context, candleID string) (bool, error)
    36  	GetCandleIDForIntervalAndMarket(ctx context.Context, interval string, market string) (bool, string, error)
    37  }
    38  
    39  type Svc struct {
    40  	Config
    41  	CandleStore
    42  	ctx context.Context
    43  	log *logging.Logger
    44  
    45  	candleIDToUpdatesStream  map[string]*CandleUpdates
    46  	subscriptionIDToCandleID map[string]string
    47  	updatesSubscriptionMutex sync.Mutex
    48  }
    49  
    50  func NewService(ctx context.Context, log *logging.Logger, config Config, candleStore CandleStore) *Svc {
    51  	log = log.Named(namedLogger)
    52  	log.SetLevel(config.Level.Get())
    53  
    54  	return &Svc{
    55  		ctx:                      ctx,
    56  		log:                      log,
    57  		Config:                   config,
    58  		CandleStore:              candleStore,
    59  		candleIDToUpdatesStream:  map[string]*CandleUpdates{},
    60  		subscriptionIDToCandleID: map[string]string{},
    61  	}
    62  }
    63  
    64  // Subscribe to a channel of new or updated candles. The subscriber id will must be retained for future reference and to Unsubscribe.
    65  func (cs *Svc) Subscribe(ctx context.Context, candleID string) (string, <-chan entities.Candle, error) {
    66  	cs.updatesSubscriptionMutex.Lock()
    67  	defer cs.updatesSubscriptionMutex.Unlock()
    68  
    69  	exists, err := cs.CandleExists(ctx, candleID)
    70  	if err != nil {
    71  		return "", nil, fmt.Errorf("subscribing to candles:%w", err)
    72  	}
    73  
    74  	if !exists {
    75  		return "", nil, fmt.Errorf("no candle exists for candle id:%s", candleID)
    76  	}
    77  
    78  	if _, ok := cs.candleIDToUpdatesStream[candleID]; !ok {
    79  		updates := NewCandleUpdates(cs.ctx, cs.log, candleID, cs, cs.Config.CandleUpdates)
    80  		cs.candleIDToUpdatesStream[candleID] = updates
    81  	}
    82  
    83  	updatesStream := cs.candleIDToUpdatesStream[candleID]
    84  	subscriptionID, out, err := updatesStream.Subscribe()
    85  	if err != nil {
    86  		return "", nil, fmt.Errorf("failed to subscribe to candle %s: %w", candleID, err)
    87  	}
    88  
    89  	cs.subscriptionIDToCandleID[subscriptionID] = candleID
    90  
    91  	return subscriptionID, out, nil
    92  }
    93  
    94  func (cs *Svc) Unsubscribe(subscriptionID string) error {
    95  	cs.updatesSubscriptionMutex.Lock()
    96  	defer cs.updatesSubscriptionMutex.Unlock()
    97  
    98  	if candleID, ok := cs.subscriptionIDToCandleID[subscriptionID]; ok {
    99  		updatesStream := cs.candleIDToUpdatesStream[candleID]
   100  		err := updatesStream.Unsubscribe(subscriptionID)
   101  		if err != nil {
   102  			return fmt.Errorf("failed to unsubscribe from candle %s: %w", candleID, err)
   103  		}
   104  		return nil
   105  	}
   106  	return fmt.Errorf("no subscription found for id %s", subscriptionID)
   107  }