github.com/maypok86/otter@v1.2.1/internal/s3fifo/policy.go (about)

     1  // Copyright (c) 2023 Alexey Mayshev. All rights reserved.
     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 s3fifo
    16  
    17  import (
    18  	"github.com/maypok86/otter/internal/generated/node"
    19  )
    20  
    21  // Policy is an eviction policy based on S3-FIFO eviction algorithm
    22  // from the following paper: https://dl.acm.org/doi/10.1145/3600006.3613147.
    23  type Policy[K comparable, V any] struct {
    24  	small                *small[K, V]
    25  	main                 *main[K, V]
    26  	ghost                *ghost[K, V]
    27  	maxCost              int
    28  	maxAvailableNodeCost int
    29  }
    30  
    31  // NewPolicy creates a new Policy.
    32  func NewPolicy[K comparable, V any](maxCost int) *Policy[K, V] {
    33  	smallMaxCost := maxCost / 10
    34  	mainMaxCost := maxCost - smallMaxCost
    35  
    36  	main := newMain[K, V](mainMaxCost)
    37  	ghost := newGhost(main)
    38  	small := newSmall(smallMaxCost, main, ghost)
    39  	ghost.small = small
    40  
    41  	return &Policy[K, V]{
    42  		small:                small,
    43  		main:                 main,
    44  		ghost:                ghost,
    45  		maxCost:              maxCost,
    46  		maxAvailableNodeCost: smallMaxCost,
    47  	}
    48  }
    49  
    50  // Read updates the eviction policy based on node accesses.
    51  func (p *Policy[K, V]) Read(nodes []node.Node[K, V]) {
    52  	for _, n := range nodes {
    53  		n.IncrementFrequency()
    54  	}
    55  }
    56  
    57  // Add adds node to the eviction policy.
    58  func (p *Policy[K, V]) Add(deleted []node.Node[K, V], n node.Node[K, V]) []node.Node[K, V] {
    59  	if p.ghost.isGhost(n) {
    60  		p.main.insert(n)
    61  		n.ResetFrequency()
    62  	} else {
    63  		p.small.insert(n)
    64  	}
    65  
    66  	for p.isFull() {
    67  		deleted = p.evict(deleted)
    68  	}
    69  
    70  	return deleted
    71  }
    72  
    73  func (p *Policy[K, V]) evict(deleted []node.Node[K, V]) []node.Node[K, V] {
    74  	if p.small.cost >= p.maxCost/10 {
    75  		return p.small.evict(deleted)
    76  	}
    77  
    78  	return p.main.evict(deleted)
    79  }
    80  
    81  func (p *Policy[K, V]) isFull() bool {
    82  	return p.small.cost+p.main.cost > p.maxCost
    83  }
    84  
    85  // Delete deletes node from the eviction policy.
    86  func (p *Policy[K, V]) Delete(n node.Node[K, V]) {
    87  	if n.IsSmall() {
    88  		p.small.remove(n)
    89  		return
    90  	}
    91  
    92  	if n.IsMain() {
    93  		p.main.remove(n)
    94  	}
    95  }
    96  
    97  // MaxAvailableCost returns the maximum available cost of the node.
    98  func (p *Policy[K, V]) MaxAvailableCost() int {
    99  	return p.maxAvailableNodeCost
   100  }
   101  
   102  // Clear clears the eviction policy and returns it to the default state.
   103  func (p *Policy[K, V]) Clear() {
   104  	p.ghost.clear()
   105  	p.main.clear()
   106  	p.small.clear()
   107  }