github.com/bhojpur/cache@v0.0.4/pkg/file/database/database.go (about)

     1  package database
     2  
     3  // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved.
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in
    13  // all copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    21  // THE SOFTWARE.
    22  
    23  import (
    24  	"encoding/json"
    25  	"io/ioutil"
    26  	"os"
    27  	"path"
    28  	"time"
    29  
    30  	"github.com/bhojpur/cache/pkg/file/common"
    31  	"github.com/bhojpur/cache/pkg/file/models"
    32  	"github.com/bhojpur/cache/pkg/file/types"
    33  	memcache "github.com/bhojpur/cache/pkg/memory"
    34  )
    35  
    36  // DBType represents the type of database of the instance, either a node's
    37  // shard database or the main file database.
    38  type DBType int
    39  
    40  const (
    41  	// FILEDB the marker for a file database.
    42  	FILEDB DBType = iota
    43  
    44  	// NSHARDDB is the marker for the node's shard database.
    45  	NSHARDDB DBType = iota
    46  )
    47  
    48  // fileDBBucket is the bucket used to store files within a filedb.
    49  var fileDBBucket = []byte("files")
    50  
    51  // nshardDBBucket is the bucket used to store shards within a shard database.
    52  var nshardDBBucket = []byte("shards")
    53  
    54  // Database implements a general database that holds various data within meros.
    55  type Database struct {
    56  	Header types.DatabaseHeader `json:"header"` // Database header info
    57  	Name   string               `json:"name"`   // The name of the db
    58  	DBType DBType               // The type of database
    59  
    60  	DB     *memcache.DB // Bhojpur Cache in-memory databse instance
    61  	bucket []byte       // The bucket for the database
    62  	open   bool         // Status of the DB
    63  }
    64  
    65  // Open opens the database for reading and writing. Creates a new DB if one
    66  // with that name does not already exist.
    67  func Open(dbName string, dbType DBType) (*Database, error) {
    68  	// Make sure path exists
    69  	err := common.CreateDirIfDoesNotExist(path.Join(models.DBPath, dbName))
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	var database *Database // The database to return
    75  
    76  	// Prepare to serialize the database struct
    77  	databasePath := path.Join(models.DBPath, dbName, "db.json")
    78  	if _, err := os.Stat(databasePath); err != nil { // If DB name does not exist
    79  		// Create the database struct
    80  		database = &Database{
    81  			Header: types.NewDatabaseHeader(dbName), // Generate and set header
    82  
    83  			Name: dbName, // Set the name
    84  		}
    85  
    86  		err = database.serialize(databasePath) // Write the db struct to disk
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  
    91  	} else {
    92  		// If the db does exist, read from it and return it
    93  		database, err = deserialize(databasePath)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  	}
    98  
    99  	// Prepare to open the Bhojpur Cache in-memory database
   100  	memdbPath := path.Join(models.DBPath, dbName, "bhojpur-cache.db")
   101  	db, err := memcache.Open(memdbPath, 0600, &memcache.Options{ // Open the DB
   102  		Timeout: 1 * time.Second,
   103  	})
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	database.DB = db         // Set the DB
   109  	database.DBType = dbType // Set the type of database
   110  
   111  	// Bucket handler
   112  	switch dbType {
   113  	case FILEDB: // If FileDB type, set to the corresponding bucket
   114  		database.bucket = fileDBBucket
   115  	case NSHARDDB: // If NodeShardDB type, set to the corresponding bucket
   116  		database.bucket = nshardDBBucket
   117  	}
   118  
   119  	err = database.makeBuckets() // Make the buckets in the database
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	database.open = true // Set the status to open
   125  
   126  	return database, nil
   127  }
   128  
   129  // Close closes the database.
   130  func (db *Database) Close() error {
   131  	err := db.DB.Close() // Close the DB
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	db.open = false // Set DB status
   137  	return nil
   138  }
   139  
   140  // makeBuckets constructs the buckets in the database.
   141  func (db *Database) makeBuckets() error {
   142  	// Create all buckets in the database
   143  	err := db.DB.Update(func(tx *memcache.Tx) error { // Open tx for bucket creation
   144  		_, err := tx.CreateBucketIfNotExists(db.bucket) // Create bucket
   145  		return err                                      // Handle err
   146  	})
   147  	if err != nil { // Check the err
   148  		return err
   149  	}
   150  	return err
   151  }
   152  
   153  // String marshals the DB as a string.
   154  func (db *Database) String() string {
   155  	json, _ := json.MarshalIndent(*db, "", "  ")
   156  	return string(json)
   157  }
   158  
   159  // serialize will serialize the database and write it to disk.
   160  func (db *Database) serialize(filepath string) error {
   161  	json, _ := json.MarshalIndent(*db, "", "  ")
   162  	err := ioutil.WriteFile(filepath, json, 0600)
   163  	return err
   164  }
   165  
   166  // deserialize will deserialize the database from the disk
   167  func deserialize(filepath string) (*Database, error) {
   168  	data, err := ioutil.ReadFile(filepath) // Read the database from disk
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	buffer := &Database{} // Initialize the database buffer
   174  
   175  	// Unmarshal and write into the buffer
   176  	err = json.Unmarshal(data, buffer)
   177  
   178  	return buffer, err
   179  }