github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/core/discovery/brokerdiscovery/storage.go (about) 1 /* 2 * Copyright (C) 2019 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package brokerdiscovery 19 20 import ( 21 "fmt" 22 "sync" 23 24 "github.com/mysteriumnetwork/node/core/discovery" 25 "github.com/mysteriumnetwork/node/core/discovery/proposal" 26 "github.com/mysteriumnetwork/node/eventbus" 27 "github.com/mysteriumnetwork/node/market" 28 ) 29 30 // ProposalReducer proposal match function 31 type ProposalReducer func(proposal market.ServiceProposal) bool 32 33 // NewStorage creates new instance of ProposalStorage 34 func NewStorage(eventPublisher eventbus.Publisher) *ProposalStorage { 35 return &ProposalStorage{ 36 eventPublisher: eventPublisher, 37 proposals: make([]market.ServiceProposal, 0), 38 } 39 } 40 41 // ProposalStorage represents table of currently active proposals in Mysterium Discovery 42 type ProposalStorage struct { 43 eventPublisher eventbus.Publisher 44 45 proposals []market.ServiceProposal 46 mutex sync.RWMutex 47 } 48 49 // Proposals returns list of proposals in storage 50 func (s *ProposalStorage) Proposals() []market.ServiceProposal { 51 s.mutex.Lock() 52 defer s.mutex.Unlock() 53 54 proposals := make([]market.ServiceProposal, 0) 55 proposals = append(proposals, s.proposals...) 56 return proposals 57 } 58 59 // MatchProposals fetches currently active service proposals from storage by match function 60 func (s *ProposalStorage) MatchProposals(match ProposalReducer) ([]market.ServiceProposal, error) { 61 s.mutex.Lock() 62 defer s.mutex.Unlock() 63 64 proposals := make([]market.ServiceProposal, 0) 65 for _, p := range s.proposals { 66 if match(p) { 67 proposals = append(proposals, p) 68 } 69 } 70 return proposals, nil 71 } 72 73 // FindProposals fetches currently active service proposals from storage by given filter 74 func (s *ProposalStorage) FindProposals(filter *proposal.Filter) ([]market.ServiceProposal, error) { 75 return s.MatchProposals(filter.Matches) 76 } 77 78 // Countries fetches currently active service proposals from storage by given filter 79 func (s *ProposalStorage) Countries(filter *proposal.Filter) (map[string]int, error) { 80 proposals, err := s.MatchProposals(filter.Matches) 81 if err != nil { 82 return nil, err 83 } 84 85 countries := make(map[string]int, 0) 86 for _, p := range proposals { 87 countries[p.Location.Country]++ 88 } 89 90 return countries, nil 91 } 92 93 // Set puts given list proposals to storage 94 func (s *ProposalStorage) Set(proposals []market.ServiceProposal) { 95 s.mutex.Lock() 96 defer s.mutex.Unlock() 97 98 proposalsOld := s.proposals 99 for _, p := range proposals { 100 index, exist := s.getProposalIndex(proposalsOld, p.UniqueID()) 101 if exist { 102 go s.eventPublisher.Publish(discovery.AppTopicProposalUpdated, p) 103 proposalsOld = append(proposalsOld[:index], proposalsOld[index+1:]...) 104 } else { 105 go s.eventPublisher.Publish(discovery.AppTopicProposalAdded, p) 106 } 107 } 108 for _, p := range proposalsOld { 109 go s.eventPublisher.Publish(discovery.AppTopicProposalRemoved, p) 110 } 111 s.proposals = proposals 112 } 113 114 // HasProposal checks if proposal exists in storage 115 func (s *ProposalStorage) HasProposal(id market.ProposalID) bool { 116 s.mutex.Lock() 117 defer s.mutex.Unlock() 118 119 _, exist := s.getProposalIndex(s.proposals, id) 120 return exist 121 } 122 123 // GetProposal returns proposal from storage 124 func (s *ProposalStorage) GetProposal(id market.ProposalID) (*market.ServiceProposal, error) { 125 s.mutex.Lock() 126 defer s.mutex.Unlock() 127 128 index, exist := s.getProposalIndex(s.proposals, id) 129 if !exist { 130 return nil, fmt.Errorf(`proposal does not exist: %v`, id) 131 } 132 return &s.proposals[index], nil 133 } 134 135 // AddProposal appends given proposal to storage 136 func (s *ProposalStorage) AddProposal(proposals ...market.ServiceProposal) { 137 s.mutex.Lock() 138 defer s.mutex.Unlock() 139 140 for _, p := range proposals { 141 if index, exist := s.getProposalIndex(s.proposals, p.UniqueID()); !exist { 142 s.eventPublisher.Publish(discovery.AppTopicProposalAdded, p) 143 s.proposals = append(s.proposals, p) 144 } else { 145 s.eventPublisher.Publish(discovery.AppTopicProposalUpdated, p) 146 s.proposals[index] = p 147 } 148 } 149 } 150 151 // RemoveProposal removes proposal from storage 152 func (s *ProposalStorage) RemoveProposal(id market.ProposalID) { 153 s.mutex.Lock() 154 defer s.mutex.Unlock() 155 156 if index, exist := s.getProposalIndex(s.proposals, id); exist { 157 go s.eventPublisher.Publish(discovery.AppTopicProposalRemoved, s.proposals[index]) 158 s.proposals = append(s.proposals[:index], s.proposals[index+1:]...) 159 } 160 } 161 162 func (s *ProposalStorage) getProposalIndex(proposals []market.ServiceProposal, id market.ProposalID) (int, bool) { 163 for index, p := range proposals { 164 if p.UniqueID() == id { 165 return index, true 166 } 167 } 168 169 return 0, false 170 }