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 }