package main

import (
	"fmt"
	"sync"
)

type Value struct {
	Version int
	Text    string
}
type Cache struct {
	mu         sync.Mutex
	generation uint64
	value      Value
	present    bool
}

func (c *Cache) Begin() uint64 { c.mu.Lock(); defer c.mu.Unlock(); return c.generation }
func (c *Cache) Invalidate()   { c.mu.Lock(); defer c.mu.Unlock(); c.generation++; c.present = false }
func (c *Cache) Fill(g uint64, v Value) bool {
	c.mu.Lock()
	defer c.mu.Unlock()
	if g != c.generation {
		return false
	}
	c.value, c.present = v, true
	return true
}
func (c *Cache) Get() (Value, bool) { c.mu.Lock(); defer c.mu.Unlock(); return c.value, c.present }

func staleRefill() (int, int) {
	db := Value{1, "old"}
	loaded := db         // A misses the cache and reads the old database row.
	db = Value{2, "new"} // B commits and invalidates an already empty cache.
	cache := loaded      // A resumes and fills after B's invalidation.
	return db.Version, cache.Version
}
func guardedRefill() (bool, int, bool, int) {
	db := Value{1, "old"}
	c := &Cache{}
	g := c.Begin()
	loaded := db
	db = Value{2, "new"}
	c.Invalidate()
	accepted := c.Fill(g, loaded)
	_, hit := c.Get()
	// A later independent read obtains current generation and current row.
	c.Fill(c.Begin(), db)
	current, _ := c.Get()
	return accepted, db.Version, hit, current.Version
}
func lostWriteBehind() (int, int) {
	db := Value{1, "old"}
	cache := Value{2, "accepted"} // Acknowledged while durable DB is still old.
	accepted := cache.Version
	cache = Value{} // Simulated process loss; volatile pending write is gone.
	_ = cache
	return accepted, db.Version
}
func main() {
	d, c := staleRefill()
	fmt.Printf("aside: db=%d cache=%d\n", d, c)
	ok, d, hit, current := guardedRefill()
	fmt.Printf("guarded: accepted=%v db=%d hit=%v next=%d\n", ok, d, hit, current)
	ack, d := lostWriteBehind()
	fmt.Printf("write-behind: acknowledged=%d durable=%d\n", ack, d)
}
