init
This commit is contained in:
275
vendor/github.com/tsuna/gohbase/caches.go
generated
vendored
Normal file
275
vendor/github.com/tsuna/gohbase/caches.go
generated
vendored
Normal file
@ -0,0 +1,275 @@
|
||||
// Copyright (C) 2016 The GoHBase Authors. All rights reserved.
|
||||
// This file is part of GoHBase.
|
||||
// Use of this source code is governed by the Apache License 2.0
|
||||
// that can be found in the COPYING file.
|
||||
|
||||
package gohbase
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/cznic/b"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tsuna/gohbase/hrpc"
|
||||
)
|
||||
|
||||
// clientRegionCache is client -> region cache. Used to quickly
|
||||
// look up all the regioninfos that map to a specific client
|
||||
type clientRegionCache struct {
|
||||
m sync.RWMutex
|
||||
|
||||
regions map[hrpc.RegionClient]map[hrpc.RegionInfo]struct{}
|
||||
}
|
||||
|
||||
// put caches client and associates a region with it. Returns a client that is in cache.
|
||||
// TODO: obvious place for optimization (use map with address as key to lookup exisiting clients)
|
||||
func (rcc *clientRegionCache) put(c hrpc.RegionClient, r hrpc.RegionInfo) hrpc.RegionClient {
|
||||
rcc.m.Lock()
|
||||
for existingClient, regions := range rcc.regions {
|
||||
// check if client already exists, checking by host and port
|
||||
// because concurrent callers might try to put the same client
|
||||
if c.Addr() == existingClient.Addr() {
|
||||
// check client already knows about the region, checking
|
||||
// by pointer is enough because we make sure that there are
|
||||
// no regions with the same name around
|
||||
if _, ok := regions[r]; !ok {
|
||||
regions[r] = struct{}{}
|
||||
}
|
||||
rcc.m.Unlock()
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"existingClient": existingClient,
|
||||
"client": c,
|
||||
}).Debug("region client is already in client's cache")
|
||||
return existingClient
|
||||
}
|
||||
}
|
||||
|
||||
// no such client yet
|
||||
rcc.regions[c] = map[hrpc.RegionInfo]struct{}{r: struct{}{}}
|
||||
rcc.m.Unlock()
|
||||
|
||||
log.WithField("client", c).Info("added new region client")
|
||||
return c
|
||||
}
|
||||
|
||||
func (rcc *clientRegionCache) del(r hrpc.RegionInfo) {
|
||||
rcc.m.Lock()
|
||||
c := r.Client()
|
||||
if c != nil {
|
||||
r.SetClient(nil)
|
||||
regions := rcc.regions[c]
|
||||
delete(regions, r)
|
||||
}
|
||||
rcc.m.Unlock()
|
||||
}
|
||||
|
||||
func (rcc *clientRegionCache) closeAll() {
|
||||
rcc.m.Lock()
|
||||
for client, regions := range rcc.regions {
|
||||
for region := range regions {
|
||||
region.MarkUnavailable()
|
||||
region.SetClient(nil)
|
||||
}
|
||||
client.Close()
|
||||
}
|
||||
rcc.m.Unlock()
|
||||
}
|
||||
|
||||
func (rcc *clientRegionCache) clientDown(c hrpc.RegionClient) map[hrpc.RegionInfo]struct{} {
|
||||
rcc.m.Lock()
|
||||
downregions, ok := rcc.regions[c]
|
||||
delete(rcc.regions, c)
|
||||
rcc.m.Unlock()
|
||||
|
||||
if ok {
|
||||
log.WithField("client", c).Info("removed region client")
|
||||
}
|
||||
return downregions
|
||||
}
|
||||
|
||||
// TODO: obvious place for optimization (use map with address as key to lookup exisiting clients)
|
||||
func (rcc *clientRegionCache) checkForClient(addr string) hrpc.RegionClient {
|
||||
rcc.m.RLock()
|
||||
|
||||
for client := range rcc.regions {
|
||||
if client.Addr() == addr {
|
||||
rcc.m.RUnlock()
|
||||
return client
|
||||
}
|
||||
}
|
||||
|
||||
rcc.m.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// key -> region cache.
|
||||
type keyRegionCache struct {
|
||||
m sync.RWMutex
|
||||
|
||||
// Maps a []byte of a region start key to a hrpc.RegionInfo
|
||||
regions *b.Tree
|
||||
}
|
||||
|
||||
func (krc *keyRegionCache) get(key []byte) ([]byte, hrpc.RegionInfo) {
|
||||
krc.m.RLock()
|
||||
|
||||
enum, ok := krc.regions.Seek(key)
|
||||
if ok {
|
||||
krc.m.RUnlock()
|
||||
log.Fatalf("WTF: got exact match for region search key %q", key)
|
||||
return nil, nil
|
||||
}
|
||||
k, v, err := enum.Prev()
|
||||
enum.Close()
|
||||
|
||||
krc.m.RUnlock()
|
||||
|
||||
if err == io.EOF {
|
||||
// we are the beginning of the tree
|
||||
return nil, nil
|
||||
}
|
||||
return k.([]byte), v.(hrpc.RegionInfo)
|
||||
}
|
||||
|
||||
func isRegionOverlap(regA, regB hrpc.RegionInfo) bool {
|
||||
// if region's stop key is empty, it's assumed to be the greatest key
|
||||
return bytes.Equal(regA.Namespace(), regB.Namespace()) &&
|
||||
bytes.Equal(regA.Table(), regB.Table()) &&
|
||||
(len(regB.StopKey()) == 0 || bytes.Compare(regA.StartKey(), regB.StopKey()) < 0) &&
|
||||
(len(regA.StopKey()) == 0 || bytes.Compare(regA.StopKey(), regB.StartKey()) > 0)
|
||||
}
|
||||
|
||||
func (krc *keyRegionCache) getOverlaps(reg hrpc.RegionInfo) []hrpc.RegionInfo {
|
||||
var overlaps []hrpc.RegionInfo
|
||||
var v interface{}
|
||||
var err error
|
||||
|
||||
// deal with empty tree in the beginning so that we don't have to check
|
||||
// EOF errors for enum later
|
||||
if krc.regions.Len() == 0 {
|
||||
return overlaps
|
||||
}
|
||||
|
||||
// check if key created from new region falls into any cached regions
|
||||
key := createRegionSearchKey(fullyQualifiedTable(reg), reg.StartKey())
|
||||
enum, ok := krc.regions.Seek(key)
|
||||
if ok {
|
||||
log.Fatalf("WTF: found a region with exact name as the search key %q", key)
|
||||
}
|
||||
|
||||
// case 1: landed before the first region in cache
|
||||
// enum.Prev() returns io.EOF
|
||||
// enum.Next() returns io.EOF
|
||||
// SeekFirst() + enum.Next() returns the first region, which has larger start key
|
||||
|
||||
// case 2: landed before the second region in cache
|
||||
// enum.Prev() returns the first region X and moves pointer to -infinity
|
||||
// enum.Next() returns io.EOF
|
||||
// SeekFirst() + enum.Next() returns first region X, which has smaller start key
|
||||
|
||||
// case 3: landed anywhere after the second region
|
||||
// enum.Prev() returns the region X before it landed, moves pointer to the region X - 1
|
||||
// enum.Next() returns X - 1 and move pointer to X, which has smaller start key
|
||||
|
||||
enum.Prev()
|
||||
_, _, err = enum.Next()
|
||||
if err == io.EOF {
|
||||
// we are in the beginning of tree, get new enum starting
|
||||
// from first region
|
||||
enum.Close()
|
||||
enum, err = krc.regions.SeekFirst()
|
||||
if err != nil {
|
||||
log.Fatalf(
|
||||
"error seeking first region when getting overlaps for region %v: %v", reg, err)
|
||||
}
|
||||
}
|
||||
|
||||
_, v, err = enum.Next()
|
||||
if isRegionOverlap(v.(hrpc.RegionInfo), reg) {
|
||||
overlaps = append(overlaps, v.(hrpc.RegionInfo))
|
||||
}
|
||||
_, v, err = enum.Next()
|
||||
|
||||
// now append all regions that overlap until the end of the tree
|
||||
// or until they don't overlap
|
||||
for err != io.EOF && isRegionOverlap(v.(hrpc.RegionInfo), reg) {
|
||||
overlaps = append(overlaps, v.(hrpc.RegionInfo))
|
||||
_, v, err = enum.Next()
|
||||
}
|
||||
enum.Close()
|
||||
return overlaps
|
||||
}
|
||||
|
||||
// put looks up if there's already region with this name in regions cache
|
||||
// and if there's, returns it in overlaps and doesn't modify the cache.
|
||||
// Otherwise, it puts the region and removes all overlaps in case all of
|
||||
// them are older. Returns a slice of overlapping regions and whether
|
||||
// passed region was put in the cache.
|
||||
func (krc *keyRegionCache) put(reg hrpc.RegionInfo) (overlaps []hrpc.RegionInfo, replaced bool) {
|
||||
krc.m.Lock()
|
||||
krc.regions.Put(reg.Name(), func(v interface{}, exists bool) (interface{}, bool) {
|
||||
if exists {
|
||||
// region is already in cache,
|
||||
// note: regions with the same name have the same age
|
||||
overlaps = []hrpc.RegionInfo{v.(hrpc.RegionInfo)}
|
||||
return nil, false
|
||||
}
|
||||
// find all entries that are overlapping with the range of the new region.
|
||||
overlaps = krc.getOverlaps(reg)
|
||||
for _, o := range overlaps {
|
||||
if o.ID() > reg.ID() {
|
||||
// overlapping region is younger,
|
||||
// don't replace any regions
|
||||
// TODO: figure out if there can a case where we might
|
||||
// have both older and younger overlapping regions, for
|
||||
// now we only replace if all overlaps are older
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
// all overlaps are older, put the new region
|
||||
replaced = true
|
||||
return reg, true
|
||||
})
|
||||
if !replaced {
|
||||
krc.m.Unlock()
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"region": reg,
|
||||
"overlaps": overlaps,
|
||||
"replaced": replaced,
|
||||
}).Debug("region is already in cache")
|
||||
return
|
||||
}
|
||||
// delete overlapping regions
|
||||
// TODO: in case overlaps are always either younger or older,
|
||||
// we can just greedily remove them in Put function
|
||||
for _, o := range overlaps {
|
||||
krc.regions.Delete(o.Name())
|
||||
// let region establishers know that they can give up
|
||||
o.MarkDead()
|
||||
}
|
||||
krc.m.Unlock()
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"region": reg,
|
||||
"overlaps": overlaps,
|
||||
"replaced": replaced,
|
||||
}).Info("added new region")
|
||||
return
|
||||
}
|
||||
|
||||
func (krc *keyRegionCache) del(reg hrpc.RegionInfo) bool {
|
||||
krc.m.Lock()
|
||||
success := krc.regions.Delete(reg.Name())
|
||||
krc.m.Unlock()
|
||||
// let region establishers know that they can give up
|
||||
reg.MarkDead()
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"region": reg,
|
||||
}).Debug("removed region")
|
||||
return success
|
||||
}
|
Reference in New Issue
Block a user