github.com/easierway/concurrent_map@v1.0.0/concurrent_map.go (about)

     1  package concurrent_map
     2  
     3  import (
     4  	"sync"
     5  )
     6  
     7  // ConcurrentMap is a thread safe map collection with better performance.
     8  // The backend map entries are separated into the different partitions.
     9  // Threads can access the different partitions safely without lock.
    10  type ConcurrentMap struct {
    11  	partitions    []*innerMap
    12  	numOfBlockets int
    13  }
    14  
    15  // Partitionable is the interface which should be implemented by key type.
    16  // It is to define how to partition the entries.
    17  type Partitionable interface {
    18  	// Value is raw value of the key
    19  	Value() interface{}
    20  
    21  	// PartitionKey is used for getting the partition to store the entry with the key.
    22  	// E.g. the key's hash could be used as its PartitionKey
    23  	// The partition for the key is partitions[(PartitionKey % m.numOfBlockets)]
    24  	//
    25  	// 1 Why not provide the default hash function for partition?
    26  	// Ans: As you known, the partition solution would impact the performance significantly.
    27  	// The proper partition solution balances the access to the different partitions and
    28  	// avoid of the hot partition. The access mode highly relates to your business.
    29  	// So, the better partition solution would just be designed according to your business.
    30  	PartitionKey() int64
    31  }
    32  
    33  type innerMap struct {
    34  	m    map[interface{}]interface{}
    35  	lock sync.RWMutex
    36  }
    37  
    38  func createInnerMap() *innerMap {
    39  	return &innerMap{
    40  		m: make(map[interface{}]interface{}),
    41  	}
    42  }
    43  
    44  func (im *innerMap) get(key Partitionable) (interface{}, bool) {
    45  	keyVal := key.Value()
    46  	im.lock.RLock()
    47  	v, ok := im.m[keyVal]
    48  	im.lock.RUnlock()
    49  	return v, ok
    50  }
    51  
    52  func (im *innerMap) set(key Partitionable, v interface{}) {
    53  	keyVal := key.Value()
    54  	im.lock.Lock()
    55  	im.m[keyVal] = v
    56  	im.lock.Unlock()
    57  }
    58  
    59  func (im *innerMap) del(key Partitionable) {
    60  	keyVal := key.Value()
    61  	im.lock.Lock()
    62  	delete(im.m, keyVal)
    63  	im.lock.Unlock()
    64  }
    65  
    66  // CreateConcurrentMap is to create a ConcurrentMap with the setting number of the partitions
    67  func CreateConcurrentMap(numOfPartitions int) *ConcurrentMap {
    68  	var partitions []*innerMap
    69  	for i := 0; i < numOfPartitions; i++ {
    70  		partitions = append(partitions, createInnerMap())
    71  	}
    72  	return &ConcurrentMap{partitions, numOfPartitions}
    73  }
    74  
    75  func (m *ConcurrentMap) getPartition(key Partitionable) *innerMap {
    76  	partitionID := key.PartitionKey() % int64(m.numOfBlockets)
    77  	return (*innerMap)(m.partitions[partitionID])
    78  }
    79  
    80  // Get is to get the value by the key
    81  func (m *ConcurrentMap) Get(key Partitionable) (interface{}, bool) {
    82  	return m.getPartition(key).get(key)
    83  }
    84  
    85  // Set is to store the KV entry to the map
    86  func (m *ConcurrentMap) Set(key Partitionable, v interface{}) {
    87  	im := m.getPartition(key)
    88  	im.set(key, v)
    89  }
    90  
    91  // Del is to delete the entries by the key
    92  func (m *ConcurrentMap) Del(key Partitionable) {
    93  	im := m.getPartition(key)
    94  	im.del(key)
    95  }