featurebase/dbshard.go
Jason Aten 123ce41840 add lmdb, Tx call stats, and prep for db/shard.
- lmdb as a backend (lmdb.go)
   (lmdb is the fastest known transactional storage backend)
 - per Tx call statics report enabled with PILOSA_CALLSTAT=true (stattx.go)
 - framework for per-shard db (dbshard.go)
 - txfactory handles any pair under blue-green testing (txfactory.go)
 - enable CGO in Dockerfiles for lmdb
2020-08-17 18:26:58 -05:00

191 lines
3.7 KiB
Go

// Copyright 2020 Pilosa Corp.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pilosa
import (
"fmt"
"sync"
)
// types to support a database file per shard
type DBnode struct {
Holder map[string]*DBholder
}
type DBholder struct {
Index map[string]*DBindex
}
type DBindex struct {
Field map[string]*DBfield
}
type DBfield struct {
View map[string]*DBview
}
type DBview struct {
Shard map[uint64]*DBshard
}
type DBWrapper interface {
DeleteDBPath(path string) error
Close() error
}
type DBRegistry interface {
OpenDBWrapper(path string) (DBWrapper, error)
}
type DBshard struct {
Path string
ID DBID
Open bool
// With RWMutex, the
// writer who calls Lock() automatically gets priority over
// any reader who arrives later, even if the lock is held
// by a reader to start with.
RWMut sync.RWMutex
W DBWrapper
ParentDBview *DBview
}
type DBID struct {
Node string
Holder string
Index string
Field string
View string
Shard uint64
}
func (id *DBID) Path() (s string) {
s = id.Node + sep + id.Holder + sep + id.Index +
sep + id.Field + sep + id.View + sep + fmt.Sprintf("%04v", id.Shard)
return
}
type DBPerShard struct {
Mu sync.Mutex
Dir string
Node map[string]*DBnode
// just flat, not buried within the Node heirarchy.
// Easily see how many we have.
Flatmap map[*DBshard]struct{}
}
func NewDBPerShard(dir string) (d *DBPerShard) {
d = &DBPerShard{
Dir: dir,
Node: make(map[string]*DBnode),
Flatmap: make(map[*DBshard]struct{}),
}
return
}
func (per *DBPerShard) GetDBshard(registry DBRegistry, id DBID) (dbs *DBshard, err error) {
per.Mu.Lock()
defer per.Mu.Unlock()
dbn, ok := per.Node[id.Node]
if !ok {
dbn = &DBnode{
Holder: make(map[string]*DBholder),
}
per.Node[id.Node] = dbn
}
dbh, ok := dbn.Holder[id.Holder]
if !ok {
dbh = &DBholder{
Index: make(map[string]*DBindex),
}
dbn.Holder[id.Holder] = dbh
}
dbi, ok := dbh.Index[id.Index]
if !ok {
dbi = &DBindex{
Field: make(map[string]*DBfield),
}
dbh.Index[id.Index] = dbi
}
dbf, ok := dbi.Field[id.Field]
if !ok {
dbf = &DBfield{
View: make(map[string]*DBview),
}
dbi.Field[id.Field] = dbf
}
dbv, ok := dbf.View[id.View]
if !ok {
dbv = &DBview{
Shard: make(map[uint64]*DBshard),
}
dbf.View[id.View] = dbv
}
dbs, ok = dbv.Shard[id.Shard]
if !ok {
dbs = &DBshard{
ParentDBview: dbv,
ID: id,
Path: per.Dir + sep + id.Path(),
}
dbv.Shard[id.Shard] = dbs
}
if !dbs.Open {
dbs.W, err = registry.OpenDBWrapper(dbs.Path)
if dbs.W != nil {
per.Flatmap[dbs] = struct{}{}
}
}
return
}
func (per *DBPerShard) Del(dbs *DBshard) (err error) {
per.Mu.Lock()
defer per.Mu.Unlock()
err = dbs.W.Close()
if err != nil {
return
}
panicOn(dbs.W.DeleteDBPath(dbs.Path))
delete(per.Flatmap, dbs)
// delete from the heirarchy
delete(dbs.ParentDBview.Shard, dbs.ID.Shard)
return
}
func (per *DBPerShard) Close() (err error) {
per.Mu.Lock()
defer per.Mu.Unlock()
for _, dbn := range per.Node {
for _, dbh := range dbn.Holder {
for _, dbi := range dbh.Index {
for _, dbf := range dbi.Field {
for _, dbv := range dbf.View {
for _, dbs := range dbv.Shard {
err = dbs.W.Close()
panicOn(err)
}
}
}
}
}
}
return
}