mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
* tweak a bunch of logging and config make overall logs less verbose and chatty 1 minute computer check-in interval 3 minute snapshot interval remove CaptureLogger as it has same functionality as buffer logger add a WithPrefix to the Logger interface so sub-services can have different prefixes * fix some lint * fix lint... confused why this is coming up now
138 lines
4 KiB
Go
138 lines
4 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/featurebasedb/featurebase/v3/dax"
|
|
"github.com/featurebasedb/featurebase/v3/logger"
|
|
)
|
|
|
|
func (c *Controller) snappingTurtleRoutine(period time.Duration, control chan struct{}, log logger.Logger) {
|
|
if period == 0 {
|
|
return // disable automatic snapshotting
|
|
}
|
|
ticker := time.NewTicker(period)
|
|
for {
|
|
select {
|
|
case <-c.stopping:
|
|
ticker.Stop()
|
|
log.Debugf("Stopping Snapping Turtle")
|
|
return
|
|
case <-ticker.C:
|
|
c.snapAll(log)
|
|
case <-control:
|
|
c.snapAll(log)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Controller) snapAll(log logger.Logger) {
|
|
start := time.Now()
|
|
defer func() {
|
|
log.Printf("full snapshot took: %v", time.Since(start))
|
|
}()
|
|
ctx := context.Background()
|
|
|
|
tx, err := c.BoltDB.BeginTx(ctx, false)
|
|
if err != nil {
|
|
log.Printf("Error getting transaction for snapping turtle: %v", err)
|
|
return
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
qdbs, err := c.Schemar.Databases(tx, "")
|
|
if err != nil {
|
|
log.Printf("couldn't get databases: %v", err)
|
|
}
|
|
|
|
for _, qdb := range qdbs {
|
|
c.snapAllForDatabase(tx, qdb.QualifiedID(), log)
|
|
}
|
|
}
|
|
|
|
func (c *Controller) snapAllForDatabase(tx dax.Transaction, qdbid dax.QualifiedDatabaseID, log logger.Logger) {
|
|
log.Debugf("snapAllForDatabase: %s", qdbid)
|
|
computeNodes, err := c.Balancer.CurrentState(tx, dax.RoleTypeCompute, qdbid)
|
|
if err != nil {
|
|
log.Printf("Error getting compute balancer state for snapping turtle: %v", err)
|
|
}
|
|
|
|
// Weird nested loop for snapshotting shard data. The reason for
|
|
// this is to avoid hotspotting each node in turn and spread the
|
|
// snapshotting load across all nodes rather than snapshotting all
|
|
// jobs on one node and then moving onto the next one.
|
|
i := 0
|
|
stillWorking := true
|
|
for stillWorking {
|
|
stillWorking = false
|
|
for _, workerInfo := range computeNodes {
|
|
if len(workerInfo.Jobs) <= i {
|
|
continue
|
|
}
|
|
stillWorking = true
|
|
j, err := decodeShard(workerInfo.Jobs[i])
|
|
if err != nil {
|
|
log.Printf("couldn't decode a shard out of the job: '%s', err: %v", workerInfo.Jobs[i], err)
|
|
}
|
|
if err := c.snapshotShardData(tx, j.t.QualifiedTableID(), j.shardNum()); err != nil {
|
|
log.Printf("Couldn't snapshot table: %s, shard: %d, error: %v", j.t, j.shardNum(), err)
|
|
}
|
|
}
|
|
i++
|
|
}
|
|
|
|
// Get all tables across all orgs/dbs so we can snapshot all keyed
|
|
// fields and look up whether a table is keyed to snapshot its
|
|
// partitions.
|
|
tables, err := c.Schemar.Tables(tx, dax.QualifiedDatabaseID{})
|
|
if err != nil {
|
|
log.Printf("Couldn't get schema for snapshotting keys: %v", err)
|
|
return
|
|
}
|
|
// snapshot keyed fields
|
|
tableMap := make(map[dax.TableKey]*dax.QualifiedTable)
|
|
for _, table := range tables {
|
|
tableMap[table.Key()] = table
|
|
for _, f := range table.Fields {
|
|
if f.StringKeys() && !f.IsPrimaryKey() {
|
|
if err := c.snapshotFieldKeys(tx, table.QualifiedID(), f.Name); err != nil {
|
|
log.Printf("Couldn't snapshot table: %s, field: %s, error: %v", table, f.Name, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get all partition jobs from balancer and snapshot table keys
|
|
// for any partition that goes with a keyed table. Doing the same
|
|
// weird nested loop thing to avoid doing all jobs on one node
|
|
// back to back.
|
|
translateNodes, err := c.Balancer.CurrentState(tx, dax.RoleTypeTranslate, qdbid)
|
|
if err != nil {
|
|
log.Printf("Error getting translate balancer state for snapping turtle: %v", err)
|
|
}
|
|
|
|
i = 0
|
|
stillWorking = true
|
|
for stillWorking {
|
|
stillWorking = false
|
|
for _, workerInfo := range translateNodes {
|
|
if len(workerInfo.Jobs) <= i {
|
|
continue
|
|
}
|
|
stillWorking = true
|
|
j, err := decodePartition(workerInfo.Jobs[i])
|
|
if err != nil {
|
|
table := tableMap[j.table()]
|
|
if table.StringKeys() {
|
|
if err := c.snapshotTableKeys(tx, table.QualifiedID(), j.partitionNum()); err != nil {
|
|
log.Printf("Couldn't snapshot table: %s, partition: %d, error: %v", table, j.partitionNum(), err)
|
|
}
|
|
}
|
|
log.Printf("couldn't decode a partition out of the job: '%s', err: %v", workerInfo.Jobs[i], err)
|
|
}
|
|
}
|
|
i++
|
|
}
|
|
log.Debugf("snapAllForDatabase complete: %s", qdbid)
|
|
}
|