github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/host/storagemanager/storagemanager.go (about) 1 // package storagemanager implements a storage manager for the host on Sia. The 2 // storage manager can add sectors, remove sectors, and manage how sectors are 3 // distributed between a number of storage folders. 4 package storagemanager 5 6 // TODO: The documentation still talks like this code is a part of the host. 7 8 import ( 9 "errors" 10 "path/filepath" 11 "sync" 12 13 "github.com/NebulousLabs/Sia/crypto" 14 "github.com/NebulousLabs/Sia/persist" 15 ) 16 17 const ( 18 // Names of the various persist files in the storage manager. 19 dbFilename = "storagemanager.db" 20 logFile = "storagemanager.log" 21 settingsFile = "storagemanager.json" 22 ) 23 24 var ( 25 // dbMetadata is the header that gets applied to the database to identify a 26 // version and indicate what type of data is being stored in the database. 27 dbMetadata = persist.Metadata{ 28 Header: "Sia Storage Manager DB", 29 Version: "0.6.0", 30 } 31 32 // persistMetadata is the header that gets added to the persist file to 33 // identify the type of file and the version of the file. 34 persistMetadata = persist.Metadata{ 35 Header: "Sia Storage Manager", 36 Version: "0.6.0", 37 } 38 39 errStorageManagerClosed = errors.New("call is disabled because storage manager is closed") 40 ) 41 42 // StorageManager tracks multiple storage folders, and is responsible for 43 // adding sectors, removing sectors, and overall managing the way that data is 44 // stored. 45 type StorageManager struct { 46 // Dependencies. 47 dependencies 48 49 // Storage management information. 50 sectorSalt crypto.Hash 51 storageFolders []*storageFolder 52 53 // Utilities. 54 db *persist.BoltDatabase 55 log *persist.Logger 56 mu sync.RWMutex 57 persistDir string 58 59 // The resource lock is held by threaded functions for the duration of 60 // their operation. Functions should grab the resource lock as a read lock 61 // unless they are planning on manipulating the 'closed' variable. 62 // Readlocks are used so that multiple functions can use resources 63 // simultaneously, but the resources are not closed until all functions 64 // accessing them have returned. 65 closed bool 66 resourceLock sync.RWMutex 67 } 68 69 // Close will shut down the storage manager. 70 func (sm *StorageManager) Close() (composedError error) { 71 // Grab the resource lock and indicate that the host is closing. Concurrent 72 // functions hold the resource lock until they terminate, meaning that no 73 // threaded function will be running by the time the resource lock is 74 // acquired. 75 sm.resourceLock.Lock() 76 closed := sm.closed 77 sm.closed = true 78 sm.resourceLock.Unlock() 79 if closed { 80 return nil 81 } 82 83 // Close the bolt database. 84 err := sm.db.Close() 85 if err != nil { 86 composedError = composeErrors(composedError, err) 87 } 88 89 // Save the latest host state. 90 sm.mu.Lock() 91 err = sm.saveSync() 92 sm.mu.Unlock() 93 if err != nil { 94 composedError = composeErrors(composedError, err) 95 } 96 97 // Close the logger. The logger should be the last thing to shut down so 98 // that all other objects have access to logging while closing. 99 err = sm.log.Close() 100 if err != nil { 101 composedError = composeErrors(composedError, err) 102 } 103 return composedError 104 } 105 106 // newStorageManager creates a new storage manager. 107 func newStorageManager(dependencies dependencies, persistDir string) (*StorageManager, error) { 108 sm := &StorageManager{ 109 dependencies: dependencies, 110 111 persistDir: persistDir, 112 } 113 114 // Create the perist directory if it does not yet exist. 115 err := dependencies.mkdirAll(sm.persistDir, 0700) 116 if err != nil { 117 return nil, err 118 } 119 120 // Initialize the logger. Logging should be initialized ASAP, because the 121 // rest of the initialization makes use of the logger. 122 sm.log, err = dependencies.newLogger(filepath.Join(sm.persistDir, logFile)) 123 if err != nil { 124 return nil, err 125 } 126 127 // Open the database containing the host's storage obligation metadata. 128 sm.db, err = dependencies.openDatabase(dbMetadata, filepath.Join(sm.persistDir, dbFilename)) 129 if err != nil { 130 // An error will be returned if the database has the wrong version, but 131 // as of writing there was only one version of the database and all 132 // other databases would be incompatible. 133 _ = sm.log.Close() 134 return nil, err 135 } 136 // After opening the database, it must be initialized. Most commonly, 137 // nothing happens. But for new databases, a set of buckets must be 138 // created. Initialization is also a good time to run sanity checks. 139 err = sm.initDB() 140 if err != nil { 141 _ = sm.log.Close() 142 _ = sm.db.Close() 143 return nil, err 144 } 145 146 // Load the prior persistence structures. 147 err = sm.load() 148 if err != nil { 149 _ = sm.log.Close() 150 _ = sm.db.Close() 151 return nil, err 152 } 153 return sm, nil 154 } 155 156 // New returns an initialized StorageManager. 157 func New(persistDir string) (*StorageManager, error) { 158 return newStorageManager(productionDependencies{}, persistDir) 159 }