github.com/ethersphere/bee/v2@v2.2.0/pkg/storage/inmemstore/inmemstore.go (about)

     1  // Copyright 2022 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package inmemstore
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"strings"
    11  	"sync"
    12  
    13  	"github.com/armon/go-radix"
    14  	"github.com/ethersphere/bee/v2/pkg/storage"
    15  )
    16  
    17  const (
    18  	separator = "/"
    19  )
    20  
    21  // Store implements an in-memory Store. We will use the hashicorp/go-radix implementation.
    22  // This pkg provides a mutable radix which gives O(k) lookup and ordered iteration.
    23  type Store struct {
    24  	st *radix.Tree
    25  	mu sync.RWMutex
    26  }
    27  
    28  func New() *Store {
    29  	return &Store{st: radix.New()}
    30  }
    31  
    32  func key(i storage.Key) string {
    33  	return strings.Join([]string{i.Namespace(), i.ID()}, separator)
    34  }
    35  
    36  func idFromKey(key, pfx string) string {
    37  	return strings.TrimPrefix(key, pfx+separator)
    38  }
    39  
    40  func (s *Store) Get(i storage.Item) error {
    41  	s.mu.RLock()
    42  	val, found := s.st.Get(key(i))
    43  	s.mu.RUnlock()
    44  	if !found {
    45  		return storage.ErrNotFound
    46  	}
    47  
    48  	err := i.Unmarshal(val.([]byte))
    49  	if err != nil {
    50  		return fmt.Errorf("failed unmarshaling item: %w", err)
    51  	}
    52  
    53  	return nil
    54  }
    55  
    56  func (s *Store) Has(k storage.Key) (bool, error) {
    57  	s.mu.RLock()
    58  	_, found := s.st.Get(key(k))
    59  	s.mu.RUnlock()
    60  
    61  	return found, nil
    62  }
    63  
    64  func (s *Store) GetSize(k storage.Key) (int, error) {
    65  	s.mu.RLock()
    66  	val, found := s.st.Get(key(k))
    67  	s.mu.RUnlock()
    68  	if !found {
    69  		return 0, storage.ErrNotFound
    70  	}
    71  
    72  	return len(val.([]byte)), nil
    73  }
    74  
    75  func (s *Store) Put(i storage.Item) error {
    76  	s.mu.Lock()
    77  	defer s.mu.Unlock()
    78  	return s.put(i)
    79  }
    80  
    81  func (s *Store) put(i storage.Item) error {
    82  	val, err := i.Marshal()
    83  	if err != nil {
    84  		return fmt.Errorf("failed marshaling item: %w", err)
    85  	}
    86  
    87  	s.st.Insert(key(i), val)
    88  	return nil
    89  }
    90  
    91  func (s *Store) Delete(i storage.Item) error {
    92  	s.mu.Lock()
    93  	defer s.mu.Unlock()
    94  	return s.delete(i)
    95  }
    96  
    97  func (s *Store) delete(i storage.Item) error {
    98  	s.st.Delete(key(i))
    99  	return nil
   100  }
   101  
   102  func (s *Store) Count(k storage.Key) (int, error) {
   103  	s.mu.RLock()
   104  	defer s.mu.RUnlock()
   105  
   106  	count := 0
   107  	s.st.WalkPrefix(k.Namespace(), func(_ string, _ interface{}) bool {
   108  		count++
   109  		return false
   110  	})
   111  	return count, nil
   112  }
   113  
   114  func (s *Store) Iterate(q storage.Query, fn storage.IterateFn) error {
   115  	if err := q.Validate(); err != nil {
   116  		return fmt.Errorf("failed iteration: %w", err)
   117  	}
   118  
   119  	getNext := func(k string, v interface{}) (*storage.Result, error) {
   120  		for _, filter := range q.Filters {
   121  			if filter(idFromKey(k, q.Factory().Namespace()), v.([]byte)) {
   122  				return nil, nil
   123  			}
   124  		}
   125  		var res *storage.Result
   126  		switch q.ItemProperty {
   127  		case storage.QueryItemID, storage.QueryItemSize:
   128  			res = &storage.Result{ID: idFromKey(k, q.Factory().Namespace()), Size: len(v.([]byte))}
   129  		case storage.QueryItem:
   130  			newItem := q.Factory()
   131  			err := newItem.Unmarshal(v.([]byte))
   132  			if err != nil {
   133  				return nil, fmt.Errorf("failed unmarshaling: %w", err)
   134  			}
   135  			res = &storage.Result{ID: idFromKey(k, q.Factory().Namespace()), Entry: newItem}
   136  		}
   137  		return res, nil
   138  	}
   139  
   140  	var prefix string
   141  	if q.PrefixAtStart {
   142  		prefix = q.Factory().Namespace()
   143  	} else {
   144  		prefix = q.Factory().Namespace() + separator + q.Prefix
   145  	}
   146  
   147  	var (
   148  		retErr       error
   149  		firstSkipped = !q.SkipFirst
   150  		skipUntil    = false
   151  	)
   152  
   153  	s.mu.RLock()
   154  	defer s.mu.RUnlock()
   155  
   156  	switch q.Order {
   157  	case storage.KeyAscendingOrder:
   158  		s.st.WalkPrefix(prefix, func(k string, v interface{}) bool {
   159  
   160  			if q.PrefixAtStart && !skipUntil {
   161  				if k >= prefix+separator+q.Prefix {
   162  					skipUntil = true
   163  				} else {
   164  					return false
   165  				}
   166  			}
   167  
   168  			if q.SkipFirst && !firstSkipped {
   169  				firstSkipped = true
   170  				return false
   171  			}
   172  
   173  			res, err := getNext(k, v)
   174  			if err != nil {
   175  				retErr = errors.Join(retErr, err)
   176  				return true
   177  			}
   178  			if res != nil {
   179  				stop, err := fn(*res)
   180  				if err != nil {
   181  					retErr = errors.Join(retErr, fmt.Errorf("failed in iterate function: %w", err))
   182  					return true
   183  				}
   184  				return stop
   185  			}
   186  			return false
   187  		})
   188  	case storage.KeyDescendingOrder:
   189  		// currently there is no optimal way to do reverse iteration. We can efficiently do forward
   190  		// iteration. So we have two options, first is to reduce time complexity by compromising
   191  		// on space complexity. So we keep track of keys and values during forward iteration
   192  		// to do a simple reverse iteration. Other option is to reduce space complexity by keeping
   193  		// track of only keys during forward iteration, then use Get to read the value on reverse
   194  		// iteration. This would involve additional complexity of doing a Get on reverse iteration.
   195  		// For now, inmem implementation is not meant to work for large datasets, so first option
   196  		// is chosen.
   197  		results := make([]storage.Result, 0)
   198  		s.st.WalkPrefix(prefix, func(k string, v interface{}) bool {
   199  			res, err := getNext(k, v)
   200  			if err != nil {
   201  				retErr = errors.Join(retErr, err)
   202  				return true
   203  			}
   204  			if res != nil {
   205  				results = append(results, *res)
   206  			}
   207  			return false
   208  		})
   209  		if retErr != nil {
   210  			break
   211  		}
   212  		for i := len(results) - 1; i >= 0; i-- {
   213  			if q.SkipFirst && !firstSkipped {
   214  				firstSkipped = true
   215  				continue
   216  			}
   217  			stop, err := fn(results[i])
   218  			if err != nil {
   219  				return fmt.Errorf("failed in iterate function: %w", err)
   220  			}
   221  			if stop {
   222  				break
   223  			}
   224  		}
   225  	}
   226  	return retErr
   227  }
   228  
   229  func (s *Store) Close() error {
   230  	return nil
   231  }