github.com/influxdata/influxdb/v2@v2.7.6/bolt/bbolt.go (about)

     1  package bolt
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"time"
     9  
    10  	platform "github.com/influxdata/influxdb/v2"
    11  	platform2 "github.com/influxdata/influxdb/v2/kit/platform"
    12  	"github.com/influxdata/influxdb/v2/rand"
    13  	"github.com/influxdata/influxdb/v2/snowflake"
    14  	bolt "go.etcd.io/bbolt"
    15  	"go.uber.org/zap"
    16  )
    17  
    18  const DefaultFilename = "influxd.bolt"
    19  
    20  // Client is a client for the boltDB data store.
    21  type Client struct {
    22  	Path string
    23  	db   *bolt.DB
    24  	log  *zap.Logger
    25  
    26  	IDGenerator    platform2.IDGenerator
    27  	TokenGenerator platform.TokenGenerator
    28  	platform.TimeGenerator
    29  
    30  	pluginsCollector *pluginMetricsCollector
    31  }
    32  
    33  // NewClient returns an instance of a Client.
    34  func NewClient(log *zap.Logger) *Client {
    35  	return &Client{
    36  		log:            log,
    37  		IDGenerator:    snowflake.NewIDGenerator(),
    38  		TokenGenerator: rand.NewTokenGenerator(64),
    39  		TimeGenerator:  platform.RealTimeGenerator{},
    40  		// Refresh telegraf plugin metrics every hour.
    41  		pluginsCollector: NewPluginMetricsCollector(time.Minute * 59),
    42  	}
    43  }
    44  
    45  // DB returns the clients DB.
    46  func (c *Client) DB() *bolt.DB {
    47  	return c.db
    48  }
    49  
    50  // Open / create boltDB file.
    51  func (c *Client) Open(ctx context.Context) error {
    52  	// Ensure the required directory structure exists.
    53  	if err := os.MkdirAll(filepath.Dir(c.Path), 0700); err != nil {
    54  		return fmt.Errorf("unable to create directory %s: %v", c.Path, err)
    55  	}
    56  
    57  	if _, err := os.Stat(c.Path); err != nil && !os.IsNotExist(err) {
    58  		return err
    59  	}
    60  
    61  	// Open database file.
    62  	db, err := bolt.Open(c.Path, 0600, &bolt.Options{Timeout: 1 * time.Second})
    63  	if err != nil {
    64  		// Hack to give a slightly nicer error message for a known failure mode when bolt calls
    65  		// mmap on a file system that doesn't support the MAP_SHARED option.
    66  		//
    67  		// See: https://github.com/boltdb/bolt/issues/272
    68  		// See: https://stackoverflow.com/a/18421071
    69  		if err.Error() == "invalid argument" {
    70  			return fmt.Errorf("unable to open boltdb: mmap of %q may not support the MAP_SHARED option", c.Path)
    71  		}
    72  
    73  		return fmt.Errorf("unable to open boltdb: %w", err)
    74  	}
    75  	c.db = db
    76  
    77  	if err := c.initialize(ctx); err != nil {
    78  		return err
    79  	}
    80  
    81  	c.pluginsCollector.Open(c.db)
    82  
    83  	c.log.Info("Resources opened", zap.String("path", c.Path))
    84  	return nil
    85  }
    86  
    87  // initialize creates Buckets that are missing
    88  func (c *Client) initialize(ctx context.Context) error {
    89  	if err := c.db.Update(func(tx *bolt.Tx) error {
    90  		// Always create ID bucket.
    91  		// TODO: is this still needed?
    92  		if err := c.initializeID(tx); err != nil {
    93  			return err
    94  		}
    95  
    96  		// TODO: make card to normalize everything under kv?
    97  		bkts := [][]byte{
    98  			authorizationBucket,
    99  			bucketBucket,
   100  			dashboardBucket,
   101  			organizationBucket,
   102  			scraperBucket,
   103  			telegrafBucket,
   104  			telegrafPluginsBucket,
   105  			remoteBucket,
   106  			replicationBucket,
   107  			userBucket,
   108  		}
   109  		for _, bktName := range bkts {
   110  			if _, err := tx.CreateBucketIfNotExists(bktName); err != nil {
   111  				return err
   112  			}
   113  		}
   114  		return nil
   115  	}); err != nil {
   116  		return err
   117  	}
   118  
   119  	return nil
   120  }
   121  
   122  // Close the connection to the bolt database
   123  func (c *Client) Close() error {
   124  	c.pluginsCollector.Close()
   125  	if c.db != nil {
   126  		return c.db.Close()
   127  	}
   128  	return nil
   129  }