github.com/bhojpur/cache@v0.0.4/templates/dashboard.go (about)

     1  package templates
     2  
     3  // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved.
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in
    13  // all copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    21  // THE SOFTWARE.
    22  
    23  // This file contains low-level Bhojpur Cache in-memory database storage engine
    24  // structs that are used for reading from the database files.
    25  
    26  import (
    27  	"fmt"
    28  	"unsafe"
    29  
    30  	memcache "github.com/bhojpur/cache/pkg/memory"
    31  )
    32  
    33  const pageHeaderSize = int(unsafe.Offsetof(((*page)(nil)).ptr))
    34  const branchPageElementSize = int(unsafe.Sizeof(branchPageElement{}))
    35  const leafPageElementSize = int(unsafe.Sizeof(leafPageElement{}))
    36  
    37  const maxAllocSize = 0xFFFFFFF
    38  const maxNodesPerPage = 65535
    39  
    40  const (
    41  	branchPageFlag   = 0x01
    42  	leafPageFlag     = 0x02
    43  	metaPageFlag     = 0x04
    44  	freelistPageFlag = 0x10
    45  )
    46  
    47  const (
    48  	bucketLeafFlag = 0x01
    49  )
    50  
    51  type pgid uint64
    52  type txid uint64
    53  
    54  type page struct {
    55  	id       pgid
    56  	flags    uint16
    57  	count    uint16
    58  	overflow uint32
    59  	ptr      uintptr
    60  }
    61  
    62  type stats struct {
    63  	inuse       int
    64  	alloc       int
    65  	utilization float64
    66  	histogram   map[int]int
    67  }
    68  
    69  // typ returns a human readable page type string used for debugging.
    70  func (p *page) typ() string {
    71  	if (p.flags & branchPageFlag) != 0 {
    72  		return "branch"
    73  	} else if (p.flags & leafPageFlag) != 0 {
    74  		return "leaf"
    75  	} else if (p.flags & metaPageFlag) != 0 {
    76  		return "meta"
    77  	} else if (p.flags & freelistPageFlag) != 0 {
    78  		return "freelist"
    79  	}
    80  	return fmt.Sprintf("unknown<%02x>", p.flags)
    81  }
    82  
    83  func (p *page) meta() *meta {
    84  	return (*meta)(unsafe.Pointer(&p.ptr))
    85  }
    86  
    87  func (p *page) leafPageElement(index uint16) *leafPageElement {
    88  	n := &((*[maxNodesPerPage]leafPageElement)(unsafe.Pointer(&p.ptr)))[index]
    89  	return n
    90  }
    91  
    92  func (p *page) branchPageElement(index uint16) *branchPageElement {
    93  	return &((*[maxNodesPerPage]branchPageElement)(unsafe.Pointer(&p.ptr)))[index]
    94  }
    95  
    96  // stats calcuates statistics for a page.
    97  func (p *page) stats(pageSize int) stats {
    98  	var s stats
    99  	s.alloc = (int(p.overflow) + 1) * pageSize
   100  	s.inuse = p.inuse()
   101  
   102  	// Calculate space utilitization
   103  	if s.alloc > 0 {
   104  		s.utilization = float64(s.inuse) / float64(s.alloc)
   105  	}
   106  
   107  	return s
   108  }
   109  
   110  // inuse returns the number of bytes used in a given page.
   111  func (p *page) inuse() int {
   112  	var n int
   113  	if (p.flags & leafPageFlag) != 0 {
   114  		n = pageHeaderSize
   115  		if p.count > 0 {
   116  			n += leafPageElementSize * int(p.count-1)
   117  			e := p.leafPageElement(p.count - 1)
   118  			n += int(e.pos + e.ksize + e.vsize)
   119  		}
   120  
   121  	} else if (p.flags & branchPageFlag) != 0 {
   122  		n = pageHeaderSize
   123  		if p.count > 0 {
   124  			n += branchPageElementSize * int(p.count-1)
   125  			e := p.branchPageElement(p.count - 1)
   126  			n += int(e.pos + e.ksize)
   127  		}
   128  	}
   129  	return n
   130  }
   131  
   132  // usage calculates a histogram of page sizes within nested pages.
   133  func usage(tx *memcache.Tx, pgid pgid) map[int]int {
   134  	m := make(map[int]int)
   135  	forEachPage(tx, pgid, func(p *page) {
   136  		m[p.inuse()]++
   137  	})
   138  	return m
   139  }
   140  
   141  // branchPageElement represents a node on a branch page.
   142  type branchPageElement struct {
   143  	pos   uint32
   144  	ksize uint32
   145  	pgid  pgid
   146  }
   147  
   148  // key returns a byte slice of the node key.
   149  func (n *branchPageElement) key() []byte {
   150  	buf := (*[maxAllocSize]byte)(unsafe.Pointer(n))
   151  	return buf[n.pos : n.pos+n.ksize]
   152  }
   153  
   154  // leafPageElement represents a node on a leaf page.
   155  type leafPageElement struct {
   156  	flags uint32
   157  	pos   uint32
   158  	ksize uint32
   159  	vsize uint32
   160  }
   161  
   162  // key returns a byte slice of the node key.
   163  func (n *leafPageElement) key() []byte {
   164  	buf := (*[maxAllocSize]byte)(unsafe.Pointer(n))
   165  	return buf[n.pos : n.pos+n.ksize]
   166  }
   167  
   168  // value returns a byte slice of the node value.
   169  func (n *leafPageElement) value() []byte {
   170  	buf := (*[maxAllocSize]byte)(unsafe.Pointer(n))
   171  	return buf[n.pos+n.ksize : n.pos+n.ksize+n.vsize]
   172  }
   173  
   174  type meta struct {
   175  	magic    uint32
   176  	version  uint32
   177  	pageSize uint32
   178  	flags    uint32
   179  	root     bucket
   180  	freelist pgid
   181  	pgid     pgid
   182  	txid     txid
   183  	checksum uint64
   184  }
   185  
   186  // bucket_ represents the memcache.Bucket type.
   187  type bucket_ struct {
   188  	*bucket
   189  }
   190  
   191  type bucket struct {
   192  	root     pgid
   193  	sequence uint64
   194  }
   195  
   196  type tx struct {
   197  	writable bool
   198  	managed  bool
   199  	db       uintptr
   200  	meta     *meta
   201  	root     bucket
   202  	// remaining fields not used.
   203  }
   204  
   205  // find locates a page using either a set of page indices or a direct page id.
   206  // It returns a list of parent page numbers if the indices are used.
   207  // It also returns the page reference.
   208  func find(tx *memcache.Tx, directID int, indexes []int) (*page, []pgid, error) {
   209  	// If a direct ID is provided then just use it.
   210  	if directID != 0 {
   211  		return pageAt(tx, pgid(directID)), nil, nil
   212  	}
   213  
   214  	// Otherwise traverse the pages index.
   215  	ids, err := pgids(tx, indexes)
   216  	if err != nil {
   217  		return nil, nil, err
   218  	}
   219  
   220  	return pageAt(tx, ids[len(ids)-1]), ids, nil
   221  }
   222  
   223  // retrieves the page from a given transaction.
   224  func pageAt(tx *memcache.Tx, id pgid) *page {
   225  	info := tx.DB().Info()
   226  	return (*page)(unsafe.Pointer(info.Data + uintptr(info.PageSize*int(id))))
   227  }
   228  
   229  // forEachPage recursively iterates over all pages starting at a given page.
   230  func forEachPage(tx *memcache.Tx, pgid pgid, fn func(*page)) {
   231  	p := pageAt(tx, pgid)
   232  	fn(p)
   233  
   234  	if (p.flags & leafPageFlag) != 0 {
   235  		for i := 0; i < int(p.count); i++ {
   236  			if e := p.leafPageElement(uint16(i)); (e.flags & bucketLeafFlag) != 0 {
   237  				if b := (*bucket)(unsafe.Pointer(&e.value()[0])); b.root != 0 {
   238  					forEachPage(tx, b.root, fn)
   239  				}
   240  			}
   241  		}
   242  	} else if (p.flags & branchPageFlag) != 0 {
   243  		for i := 0; i < int(p.count); i++ {
   244  			forEachPage(tx, p.branchPageElement(uint16(i)).pgid, fn)
   245  		}
   246  	}
   247  }