code.vegaprotocol.io/vega@v0.79.0/datanode/service/market_data.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 service
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"sync"
    22  	"time"
    23  
    24  	"code.vegaprotocol.io/vega/datanode/entities"
    25  	"code.vegaprotocol.io/vega/datanode/utils"
    26  	"code.vegaprotocol.io/vega/logging"
    27  )
    28  
    29  type MarketDataStore interface {
    30  	Add(data *entities.MarketData) error
    31  	Flush(ctx context.Context) ([]*entities.MarketData, error)
    32  	GetMarketDataByID(ctx context.Context, marketID string) (entities.MarketData, error)
    33  	GetMarketsData(ctx context.Context) ([]entities.MarketData, error)
    34  	GetHistoricMarketData(ctx context.Context, marketID string, start, end *time.Time, pagination entities.Pagination) ([]entities.MarketData, entities.PageInfo, error)
    35  }
    36  
    37  type MarketData struct {
    38  	store     MarketDataStore
    39  	observer  utils.Observer[*entities.MarketData]
    40  	cache     map[entities.MarketID]*entities.MarketData
    41  	cacheLock sync.RWMutex
    42  }
    43  
    44  func NewMarketData(store MarketDataStore, log *logging.Logger) *MarketData {
    45  	return &MarketData{
    46  		store:    store,
    47  		observer: utils.NewObserver[*entities.MarketData]("market_data", log, 0, 0),
    48  		cache:    make(map[entities.MarketID]*entities.MarketData),
    49  	}
    50  }
    51  
    52  func (m *MarketData) Add(data *entities.MarketData) error {
    53  	if err := m.store.Add(data); err != nil {
    54  		return err
    55  	}
    56  	m.cacheLock.Lock()
    57  	m.cache[data.Market] = data
    58  	m.cacheLock.Unlock()
    59  	return nil
    60  }
    61  
    62  func (m *MarketData) Flush(ctx context.Context) error {
    63  	flushed, err := m.store.Flush(ctx)
    64  	if err != nil {
    65  		return err
    66  	}
    67  	m.observer.Notify(flushed)
    68  	return nil
    69  }
    70  
    71  func (m *MarketData) Initialise(ctx context.Context) error {
    72  	m.cacheLock.Lock()
    73  	defer m.cacheLock.Unlock()
    74  
    75  	all, err := m.store.GetMarketsData(ctx)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	for i := 0; i < len(all); i++ {
    80  		m.cache[all[i].Market] = &all[i]
    81  	}
    82  	return nil
    83  }
    84  
    85  func (m *MarketData) GetMarketDataByID(ctx context.Context, marketID string) (entities.MarketData, error) {
    86  	m.cacheLock.RLock()
    87  	defer m.cacheLock.RUnlock()
    88  
    89  	data, ok := m.cache[entities.MarketID(marketID)]
    90  	if !ok {
    91  		return entities.MarketData{}, fmt.Errorf("no market data for market: %v", marketID)
    92  	}
    93  	return *data, nil
    94  }
    95  
    96  func (m *MarketData) GetMarketsData(ctx context.Context) ([]entities.MarketData, error) {
    97  	m.cacheLock.RLock()
    98  	defer m.cacheLock.RUnlock()
    99  
   100  	data := make([]entities.MarketData, 0, len(m.cache))
   101  	for _, v := range m.cache {
   102  		data = append(data, *v)
   103  	}
   104  	return data, nil
   105  }
   106  
   107  func (m *MarketData) GetHistoricMarketData(ctx context.Context, marketID string, start, end *time.Time, pagination entities.Pagination) ([]entities.MarketData, entities.PageInfo, error) {
   108  	return m.store.GetHistoricMarketData(ctx, marketID, start, end, pagination)
   109  }
   110  
   111  func (m *MarketData) ObserveMarketData(
   112  	ctx context.Context, retries int, marketID []string,
   113  ) (<-chan []*entities.MarketData, uint64) {
   114  	markets := map[string]bool{}
   115  	for _, id := range marketID {
   116  		markets[id] = true
   117  	}
   118  
   119  	ch, ref := m.observer.Observe(ctx,
   120  		retries,
   121  		func(md *entities.MarketData) bool { return markets[md.Market.String()] })
   122  	return ch, ref
   123  }