github.com/m3db/m3@v1.5.0/src/m3ninx/index/segment/mem/concurrent_postings_map.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package mem 22 23 import ( 24 "regexp" 25 "sync" 26 27 "github.com/m3db/m3/src/m3ninx/postings" 28 "github.com/m3db/m3/src/m3ninx/postings/roaring" 29 ) 30 31 // concurrentPostingsMap is a thread-safe map from []byte -> postings.List. 32 type concurrentPostingsMap struct { 33 sync.RWMutex 34 *postingsMap 35 36 opts Options 37 } 38 39 // newConcurrentPostingsMap returns a new thread-safe map from []byte -> postings.List. 40 func newConcurrentPostingsMap(opts Options) *concurrentPostingsMap { 41 return &concurrentPostingsMap{ 42 postingsMap: newPostingsMap(opts.InitialCapacity()), 43 opts: opts, 44 } 45 } 46 47 // Add adds the provided `id` to the postings.List backing `key`. 48 func (m *concurrentPostingsMap) Add(key []byte, id postings.ID) error { 49 // Try read lock to see if we already have a postings list for the given value. 50 m.RLock() 51 p, ok := m.postingsMap.Get(key) 52 m.RUnlock() 53 54 // We have a postings list, insert the ID and move on. 55 if ok { 56 return p.Insert(id) 57 } 58 59 // A corresponding postings list doesn't exist, time to acquire write lock. 60 m.Lock() 61 p, ok = m.postingsMap.Get(key) 62 63 // Check if the corresponding postings list has been created since we released lock. 64 if ok { 65 m.Unlock() 66 return p.Insert(id) 67 } 68 69 // Create a new posting list for the term, and insert into fieldValues. 70 p = m.opts.PostingsListPool().Get() 71 m.postingsMap.SetUnsafe(key, p, postingsMapSetUnsafeOptions{ 72 NoCopyKey: true, 73 NoFinalizeKey: true, 74 }) 75 m.Unlock() 76 return p.Insert(id) 77 } 78 79 // Keys returns the keys known to the map. 80 func (m *concurrentPostingsMap) Keys() *termsIter { 81 m.RLock() 82 defer m.RUnlock() 83 keys := m.opts.BytesSliceArrayPool().Get() 84 for _, entry := range m.Iter() { 85 keys = append(keys, entry.Key()) 86 } 87 return newTermsIter(keys, m, m.opts) 88 } 89 90 // Get returns the postings.List backing `key`. 91 func (m *concurrentPostingsMap) Get(key []byte) (postings.List, bool) { 92 m.RLock() 93 p, ok := m.postingsMap.Get(key) 94 m.RUnlock() 95 if ok { 96 return p, true 97 } 98 return nil, false 99 } 100 101 // GetRegex returns the union of the postings lists whose keys match the 102 // provided regexp. 103 func (m *concurrentPostingsMap) GetRegex(re *regexp.Regexp) (postings.List, bool) { 104 lists := make([]postings.List, 0, m.postingsMap.Len()) 105 106 m.RLock() 107 for _, mapEntry := range m.postingsMap.Iter() { 108 // TODO: Evaluate lock contention caused by holding on to the read lock while 109 // evaluating this predicate. 110 // TODO: Evaluate if performing a prefix match would speed up the common case. 111 if re.Match(mapEntry.Key()) { 112 lists = append(lists, mapEntry.Value()) 113 } 114 } 115 m.RUnlock() 116 117 if len(lists) == 0 { 118 return nil, false 119 } 120 121 result, _ := roaring.Union(lists) 122 return result, true 123 }