github.com/Tri-stone/burrow@v0.25.0/execution/names/cache.go (about)

     1  // Copyright 2017 Monax Industries Limited
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package names
    16  
    17  import (
    18  	"fmt"
    19  	"sort"
    20  	"sync"
    21  )
    22  
    23  // The Cache helps prevent unnecessary IAVLTree updates and garbage generation.
    24  type Cache struct {
    25  	sync.RWMutex
    26  	backend Reader
    27  	names   map[string]*nameInfo
    28  }
    29  
    30  type nameInfo struct {
    31  	sync.RWMutex
    32  	entry   *Entry
    33  	removed bool
    34  	updated bool
    35  }
    36  
    37  var _ Writer = &Cache{}
    38  
    39  // Returns a Cache that wraps an underlying NameRegCacheGetter to use on a cache miss, can write to an
    40  // output Writer via Sync.
    41  // Not goroutine safe, use syncStateCache if you need concurrent access
    42  func NewCache(backend Reader) *Cache {
    43  	return &Cache{
    44  		backend: backend,
    45  		names:   make(map[string]*nameInfo),
    46  	}
    47  }
    48  
    49  func (cache *Cache) GetName(name string) (*Entry, error) {
    50  	nameInfo, err := cache.get(name)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	nameInfo.RLock()
    55  	defer nameInfo.RUnlock()
    56  	if nameInfo.removed {
    57  		return nil, nil
    58  	}
    59  	return nameInfo.entry, nil
    60  }
    61  
    62  func (cache *Cache) UpdateName(entry *Entry) error {
    63  	nameInfo, err := cache.get(entry.Name)
    64  	if err != nil {
    65  		return err
    66  	}
    67  	nameInfo.Lock()
    68  	defer nameInfo.Unlock()
    69  	if nameInfo.removed {
    70  		return fmt.Errorf("UpdateName on a removed name: %s", nameInfo.entry.Name)
    71  	}
    72  
    73  	nameInfo.entry = entry
    74  	nameInfo.updated = true
    75  	return nil
    76  }
    77  
    78  func (cache *Cache) RemoveName(name string) error {
    79  	nameInfo, err := cache.get(name)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	nameInfo.Lock()
    84  	defer nameInfo.Unlock()
    85  	if nameInfo.removed {
    86  		return fmt.Errorf("RemoveName on removed name: %s", name)
    87  	}
    88  	nameInfo.removed = true
    89  	return nil
    90  }
    91  
    92  // Writes whatever is in the cache to the output Writer state. Does not flush the cache, to do that call Reset()
    93  // after Sync or use Flusth if your wish to use the output state as your next backend
    94  func (cache *Cache) Sync(state Writer) error {
    95  	cache.Lock()
    96  	defer cache.Unlock()
    97  	// Determine order for names
    98  	// note names may be of any length less than some limit
    99  	var names sort.StringSlice
   100  	for nameStr := range cache.names {
   101  		names = append(names, nameStr)
   102  	}
   103  	sort.Stable(names)
   104  
   105  	// Update or delete names.
   106  	for _, name := range names {
   107  		nameInfo := cache.names[name]
   108  		nameInfo.RLock()
   109  		if nameInfo.removed {
   110  			err := state.RemoveName(name)
   111  			if err != nil {
   112  				nameInfo.RUnlock()
   113  				return err
   114  			}
   115  		} else if nameInfo.updated {
   116  			err := state.UpdateName(nameInfo.entry)
   117  			if err != nil {
   118  				nameInfo.RUnlock()
   119  				return err
   120  			}
   121  		}
   122  		nameInfo.RUnlock()
   123  	}
   124  	return nil
   125  }
   126  
   127  // Resets the cache to empty initialising the backing map to the same size as the previous iteration.
   128  func (cache *Cache) Reset(backend Reader) {
   129  	cache.Lock()
   130  	defer cache.Unlock()
   131  	cache.backend = backend
   132  	cache.names = make(map[string]*nameInfo)
   133  }
   134  
   135  // Syncs the Cache and Resets it to use Writer as the backend Reader
   136  func (cache *Cache) Flush(output Writer, backend Reader) error {
   137  	err := cache.Sync(output)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	cache.Reset(backend)
   142  	return nil
   143  }
   144  
   145  func (cache *Cache) Backend() Reader {
   146  	return cache.backend
   147  }
   148  
   149  // Get the cache accountInfo item creating it if necessary
   150  func (cache *Cache) get(name string) (*nameInfo, error) {
   151  	cache.RLock()
   152  	nmeInfo := cache.names[name]
   153  	cache.RUnlock()
   154  	if nmeInfo == nil {
   155  		cache.Lock()
   156  		defer cache.Unlock()
   157  		nmeInfo = cache.names[name]
   158  		if nmeInfo == nil {
   159  			entry, err := cache.backend.GetName(name)
   160  			if err != nil {
   161  				return nil, err
   162  			}
   163  			nmeInfo = &nameInfo{
   164  				entry: entry,
   165  			}
   166  			cache.names[name] = nmeInfo
   167  		}
   168  	}
   169  	return nmeInfo, nil
   170  }