Commit d56fe396 authored by Kirill Smelkov's avatar Kirill Smelkov

X move to zodb/storage.Cache & xcontainer/list

parent 1e40e46d
......@@ -17,27 +17,30 @@
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package client
// base for intrusive list
// Package list provides intrusive double-linked lists.
package list
// listHead is a list head entry for an element in an intrusive doubly-linked list.
// Head is a list head entry for an element in an intrusive doubly-linked list.
//
// XXX doc how to get to container of this list head via unsafe.OffsetOf
//
// always call Init() to initialize a head before using it.
type listHead struct {
type Head struct {
// XXX needs to be created with .next = .prev = self
next, prev *listHead
next, prev *Head
}
func (h *Head) Next() *Head { return h.next }
func (h *Head) Prev() *Head { return h.prev }
// Init initializes a head making it point to itself via .next and .prev
func (h *listHead) Init() {
func (h *Head) Init() {
h.next = h
h.prev = h
}
// Delete deletes h from its list
func (h *listHead) Delete() {
func (h *Head) Delete() {
h.next.prev = h.prev
h.prev.next = h.next
h.Init()
......@@ -45,7 +48,7 @@ func (h *listHead) Delete() {
// MoveBefore moves a to be before b
// XXX ok to move if a was not previously on the list?
func (a *listHead) MoveBefore(b *listHead) {
func (a *Head) MoveBefore(b *Head) {
a.Delete()
a.next = b
......
......@@ -17,7 +17,7 @@
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package client
package storage
// cache management
// XXX gotrace ... -> gotrace gen ...
......@@ -29,19 +29,16 @@ import (
"sync"
"unsafe"
// "github.com/kylelemons/godebug/pretty"
"lab.nexedi.com/kirr/neo/go/zodb"
"lab.nexedi.com/kirr/neo/go/xcommon/xcontainer/list"
)
// storLoader represents loading part of a storage
// XXX -> zodb?
type storLoader interface {
Load(xid zodb.Xid) (data []byte, serial zodb.Tid, err error)
}
// TODO maintain nhit / nmiss + way to read cache stats
// Cache adds RAM caching layer over a storage
// Cache adds RAM caching layer over a storage.
type Cache struct {
loader storLoader
loader StorLoader
mu sync.RWMutex
......@@ -55,7 +52,7 @@ type Cache struct {
gcCh chan struct{} // signals gc to run
gcMu sync.Mutex
lru listHead // revCacheEntries in LRU order
lru lruHead // revCacheEntries in LRU order
size int // cached data size in bytes
sizeMax int // cache is allowed to occupy not more than this
......@@ -80,7 +77,7 @@ type oidCacheEntry struct {
// revCacheEntry is information about 1 cached oid revision
type revCacheEntry struct {
parent *oidCacheEntry // oidCacheEntry holding us
inLRU listHead // in Cache.lru; protected by Cache.gcMu
inLRU lruHead // in Cache.lru; protected by Cache.gcMu
// we know that loadBefore(oid, .before) will give this .serial:oid.
//
......@@ -107,16 +104,20 @@ type revCacheEntry struct {
accounted bool // whether rce size accounted in cache size; protected by .parent's lock
}
// StorLoader represents loading part of a storage.
// XXX -> zodb?
type StorLoader interface {
Load(xid zodb.Xid) (data []byte, serial zodb.Tid, err error)
}
// lock order: Cache.mu > oidCacheEntry
// Cache.gcMu > oidCacheEntry
// XXX maintain nhit / nmiss?
// NewCache creates new cache backed up by loader.
//
// The cache will use not more than ~ sizeMax bytes of RAM for cached data.
func NewCache(loader storLoader, sizeMax int) *Cache {
func NewCache(loader StorLoader, sizeMax int) *Cache {
c := &Cache{
loader: loader,
entryMap: make(map[zodb.Oid]*oidCacheEntry),
......@@ -146,7 +147,7 @@ func (c *Cache) SetSizeMax(sizeMax int) {
// Load loads data from database via cache.
//
// If data is already in cache cached content is returned.
// If data is already in cache - cached content is returned.
func (c *Cache) Load(xid zodb.Xid) (data []byte, serial zodb.Tid, err error) {
rce, rceNew := c.lookupRCE(xid)
......@@ -154,7 +155,7 @@ func (c *Cache) Load(xid zodb.Xid) (data []byte, serial zodb.Tid, err error) {
if !rceNew {
<-rce.ready
c.gcMu.Lock()
rce.inLRU.MoveBefore(&c.lru)
rce.inLRU.MoveBefore(&c.lru.Head)
c.gcMu.Unlock()
// rce is not in cache - this goroutine becomes responsible for loading it
......@@ -366,23 +367,19 @@ func (c *Cache) loadRCE(rce *revCacheEntry, oid zodb.Oid) {
// update lru & cache size
gcrun := false
c.gcMu.Lock()
//xv1 := map[string]interface{}{"lru": &c.lru, "rce": &rce.inLRU}
//fmt.Printf("aaa:\n%s\n", pretty.Sprint(xv1))
if rcePrevDropped != nil {
rcePrevDropped.inLRU.Delete()
}
if !rceDropped {
rce.inLRU.MoveBefore(&c.lru)
rce.inLRU.MoveBefore(&c.lru.Head)
}
//xv2 := map[string]interface{}{"lru": &c.lru, "rce": &rce.inLRU}
//fmt.Printf("\n--------\n%s\n\n\n", pretty.Sprint(xv2))
c.size += δsize
if c.size > c.sizeMax {
gcrun = true
}
c.gcMu.Unlock()
c.gcMu.Unlock()
if gcrun {
c.gcsignal()
}
......@@ -495,7 +492,7 @@ func (c *Cache) gc() {
}
// kill 1 least-used rce
h := c.lru.next
h := c.lru.Next()
if h == &c.lru {
panic("cache: gc: empty .lru but .size > .sizeMax")
}
......@@ -608,8 +605,17 @@ func (rce *revCacheEntry) userErr(xid zodb.Xid) error {
return rce.err
}
// list head that knows it is in revCacheEntry.inLRU
type lruHead struct {
list.Head
}
// XXX vvv strictly speaking -unsafe.Offsetof(h.Head)
func (h *lruHead) Next() *lruHead { return (*lruHead)(unsafe.Pointer(h.Head.Next())) }
func (h *lruHead) Prev() *lruHead { return (*lruHead)(unsafe.Pointer(h.Head.Prev())) }
// revCacheEntry: .inLRU -> .
func (h *listHead) rceFromInLRU() (rce *revCacheEntry) {
func (h *lruHead) rceFromInLRU() (rce *revCacheEntry) {
urce := unsafe.Pointer(uintptr(unsafe.Pointer(h)) - unsafe.Offsetof(rce.inLRU))
return (*revCacheEntry)(urce)
}
......
......@@ -17,7 +17,7 @@
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
package client
package storage
import (
"bytes"
......@@ -199,10 +199,10 @@ func TestCache(t *testing.T) {
t.Helper()
size := 0
var mruv []*revCacheEntry
for hp, h := &c.lru, c.lru.prev; h != &c.lru; hp, h = h, h.prev {
for hp, h := &c.lru, c.lru.Prev(); h != &c.lru; hp, h = h, h.Prev() {
//xv := []interface{}{&c.lru, h.rceFromInLRU()}
//debug.Print(xv) // &c.lru, h.rceFromInLRU())
if h.next != hp {
if h.Next() != hp {
t.Fatalf("LRU list .next/.prev broken for\nh:\n%s\n\nhp:\n%s\n",
debug.Sprint(h), debug.Sprint(hp))
}
......
// Copyright (C) 2017 Nexedi SA and Contributors.
// Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.
// Package storage provides common bits related to ZODB storages. XXX text
package storage
// Code generated by lab.nexedi.com/kirr/go123/tracing/cmd/gotrace; DO NOT EDIT.
package client
package storage
// code generated for tracepoints
import (
......@@ -63,4 +63,4 @@ func traceCacheGCStart_Attach(pg *tracing.ProbeGroup, probe func(c *Cache)) *tra
}
// trace export signature
func _trace_exporthash_624de1c8d179b91f695f79fec7f7cdb7386501f4() {}
func _trace_exporthash_46a80e8af5056736069c296a95ad4c94388ab850() {}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment