added locks for databases; refactoring
This commit is contained in:
parent
1832d06382
commit
b9a2ca4207
206
jsondb.go
206
jsondb.go
@ -8,6 +8,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.0x0001f346.de/andreas/utils"
|
"git.0x0001f346.de/andreas/utils"
|
||||||
)
|
)
|
||||||
@ -18,62 +19,81 @@ type Database map[string]interface{}
|
|||||||
var cluster Cluster = Cluster{}
|
var cluster Cluster = Cluster{}
|
||||||
var locationOfCluster string = utils.GetPathToParrentFolderOfThisExecutable()
|
var locationOfCluster string = utils.GetPathToParrentFolderOfThisExecutable()
|
||||||
var nameOfCluster string = "jsondb"
|
var nameOfCluster string = "jsondb"
|
||||||
|
var databaseIsLocked map[string]bool = map[string]bool{}
|
||||||
|
|
||||||
const databaseExtension string = ".json"
|
const databaseExtension string = ".json"
|
||||||
|
|
||||||
// CreateDatabase creates a database or returns nil if it already exists
|
// CreateDatabase creates a database or returns nil if it already exists
|
||||||
func CreateDatabase(name string) error {
|
func CreateDatabase(nameOfDatabase string) error {
|
||||||
_, databaseAlreadyExists := cluster[name]
|
_, databaseAlreadyExists := cluster[nameOfDatabase]
|
||||||
if databaseAlreadyExists {
|
if databaseAlreadyExists {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := safeDatabase(name, Database{})
|
err := safeDatabase(nameOfDatabase, Database{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteDatabase deletes a database
|
// DeleteDatabase deletes a database
|
||||||
func DeleteDatabase(nameOfDatabase string) error {
|
func DeleteDatabase(nameOfDatabase string) error {
|
||||||
err := os.Remove(getFullPathToDatabase(nameOfDatabase))
|
_, databaseExists := cluster[nameOfDatabase]
|
||||||
|
if !databaseExists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lockDatabase(nameOfDatabase)
|
||||||
|
|
||||||
|
err := deleteDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(cluster, nameOfDatabase)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteKeyFromDatabase deletes a key in a database and write it to file
|
// DeleteKeyFromDatabase deletes a key in a database and write it to file
|
||||||
func DeleteKeyFromDatabase(nameOfDatabase string, key string) error {
|
func DeleteKeyFromDatabase(nameOfDatabase string, key string) error {
|
||||||
|
_, databaseExists := cluster[nameOfDatabase]
|
||||||
|
if !databaseExists {
|
||||||
|
return errors.New("database does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
lockDatabase(nameOfDatabase)
|
||||||
|
|
||||||
err := ReloadDatabase(nameOfDatabase)
|
err := ReloadDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
database, err := getDeepCopyOfDatabase(nameOfDatabase)
|
database, err := getDeepCopyOfDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(database, key)
|
delete(database, key)
|
||||||
err = safeDatabase(nameOfDatabase, database)
|
err = safeDatabase(nameOfDatabase, database)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster[nameOfDatabase] = database
|
cluster[nameOfDatabase] = database
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDatabase returns a database if it exists
|
// GetDatabase returns a database if it exists
|
||||||
func GetDatabase(name string) (Database, error) {
|
func GetDatabase(nameOfDatabase string) (Database, error) {
|
||||||
database, err := getDeepCopyOfDatabase(name)
|
database, err := getDeepCopyOfDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -127,64 +147,89 @@ func LoadCluster() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cluster[databaseName] = database
|
cluster[databaseName] = database
|
||||||
|
databaseIsLocked[databaseName] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReloadDatabase reloads a database from file
|
// ReloadDatabase reloads a database from file
|
||||||
func ReloadDatabase(name string) error {
|
func ReloadDatabase(nameOfDatabase string) error {
|
||||||
_, databaseExists := cluster[name]
|
_, databaseExists := cluster[nameOfDatabase]
|
||||||
if !databaseExists {
|
if !databaseExists {
|
||||||
return errors.New("database does not exist")
|
return errors.New("database does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
database, err := loadDatabase(name)
|
database, err := loadDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster[name] = database
|
cluster[nameOfDatabase] = database
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenameDatabase renames a database
|
// RenameDatabase renames a database
|
||||||
func RenameDatabase(nameOfDatabase string, newNameOfDatabase string) error {
|
func RenameDatabase(nameOfDatabase string, newNameOfDatabase string) error {
|
||||||
|
_, databaseExists := cluster[nameOfDatabase]
|
||||||
|
if !databaseExists {
|
||||||
|
return errors.New("database does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
lockDatabase(nameOfDatabase)
|
||||||
|
|
||||||
err := ReloadDatabase(nameOfDatabase)
|
err := ReloadDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
database, err := getDeepCopyOfDatabase(nameOfDatabase)
|
database, err := getDeepCopyOfDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = safeDatabase(newNameOfDatabase, database)
|
err = safeDatabase(newNameOfDatabase, database)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster[newNameOfDatabase] = database
|
cluster[newNameOfDatabase] = database
|
||||||
|
lockDatabase(newNameOfDatabase)
|
||||||
|
|
||||||
err = DeleteDatabase(nameOfDatabase)
|
err = deleteDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
|
unlockDatabase(newNameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unlockDatabase(newNameOfDatabase)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenameKeyInDatabase renames a key in a database and write it to file
|
// RenameKeyInDatabase renames a key in a database and write it to file
|
||||||
func RenameKeyInDatabase(nameOfDatabase string, keyOld string, keyNew string) error {
|
func RenameKeyInDatabase(nameOfDatabase string, keyOld string, keyNew string) error {
|
||||||
|
_, databaseExists := cluster[nameOfDatabase]
|
||||||
|
if !databaseExists {
|
||||||
|
return errors.New("database does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
lockDatabase(nameOfDatabase)
|
||||||
|
|
||||||
err := ReloadDatabase(nameOfDatabase)
|
err := ReloadDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
database, err := getDeepCopyOfDatabase(nameOfDatabase)
|
database, err := getDeepCopyOfDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,50 +237,67 @@ func RenameKeyInDatabase(nameOfDatabase string, keyOld string, keyNew string) er
|
|||||||
delete(database, keyOld)
|
delete(database, keyOld)
|
||||||
err = safeDatabase(nameOfDatabase, database)
|
err = safeDatabase(nameOfDatabase, database)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster[nameOfDatabase] = database
|
cluster[nameOfDatabase] = database
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDatabase sets a database and writes it to file
|
// SetDatabase sets a database and writes it to file
|
||||||
func SetDatabase(name string, database Database) error {
|
func SetDatabase(nameOfDatabase string, database Database) error {
|
||||||
_, databaseExists := cluster[name]
|
_, databaseExists := cluster[nameOfDatabase]
|
||||||
if !databaseExists {
|
if !databaseExists {
|
||||||
return errors.New("database does not exist")
|
return errors.New("database does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := safeDatabase(name, database)
|
lockDatabase(nameOfDatabase)
|
||||||
|
|
||||||
|
err := safeDatabase(nameOfDatabase, database)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster[name] = database
|
cluster[nameOfDatabase] = database
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetKeyInDatabase sets a key from in a database and write it to file
|
// SetKeyInDatabase sets a key from in a database and write it to file
|
||||||
func SetKeyInDatabase(nameOfDatabase string, key string, value interface{}) error {
|
func SetKeyInDatabase(nameOfDatabase string, key string, value interface{}) error {
|
||||||
|
_, databaseExists := cluster[nameOfDatabase]
|
||||||
|
if !databaseExists {
|
||||||
|
return errors.New("database does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
lockDatabase(nameOfDatabase)
|
||||||
|
|
||||||
err := ReloadDatabase(nameOfDatabase)
|
err := ReloadDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
database, err := getDeepCopyOfDatabase(nameOfDatabase)
|
database, err := getDeepCopyOfDatabase(nameOfDatabase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
database[key] = value
|
database[key] = value
|
||||||
err = safeDatabase(nameOfDatabase, database)
|
err = safeDatabase(nameOfDatabase, database)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster[nameOfDatabase] = database
|
cluster[nameOfDatabase] = database
|
||||||
|
unlockDatabase(nameOfDatabase)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -262,6 +324,50 @@ func SetNameOfCluster(name string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createCluster() error {
|
||||||
|
if utils.DoesFolderExist(getFullPathToCluster()) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := utils.CreateFolder(getFullPathToCluster())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteDatabase(nameOfDatabase string) error {
|
||||||
|
_, databaseExists := cluster[nameOfDatabase]
|
||||||
|
if !databaseExists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := os.Remove(getFullPathToDatabase(nameOfDatabase))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(cluster, nameOfDatabase)
|
||||||
|
delete(databaseIsLocked, nameOfDatabase)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDeepCopyOfDatabase(nameOfDatabase string) (Database, error) {
|
||||||
|
originalDatabase, databaseExists := cluster[nameOfDatabase]
|
||||||
|
if !databaseExists {
|
||||||
|
return nil, errors.New("database does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
copyOfDatabase := make(Database)
|
||||||
|
for key, value := range originalDatabase {
|
||||||
|
copyOfDatabase[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return copyOfDatabase, nil
|
||||||
|
}
|
||||||
|
|
||||||
func getFullPathToCluster() string {
|
func getFullPathToCluster() string {
|
||||||
return filepath.Join(
|
return filepath.Join(
|
||||||
locationOfCluster,
|
locationOfCluster,
|
||||||
@ -269,10 +375,10 @@ func getFullPathToCluster() string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFullPathToDatabase(name string) string {
|
func getFullPathToDatabase(nameOfDatabase string) string {
|
||||||
return filepath.Join(
|
return filepath.Join(
|
||||||
getFullPathToCluster(),
|
getFullPathToCluster(),
|
||||||
fmt.Sprintf("%s%s", name, databaseExtension),
|
fmt.Sprintf("%s%s", nameOfDatabase, databaseExtension),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,22 +400,9 @@ func isValidName(s string) bool {
|
|||||||
return !utils.DoesStringContainsNonWhitelistedSubstrings(s, whitelist)
|
return !utils.DoesStringContainsNonWhitelistedSubstrings(s, whitelist)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createCluster() error {
|
func loadDatabase(nameOfDatabase string) (Database, error) {
|
||||||
if utils.DoesFolderExist(getFullPathToCluster()) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := utils.CreateFolder(getFullPathToCluster())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadDatabase(name string) (Database, error) {
|
|
||||||
dbContentAsString, err := utils.LoadStringFromFile(
|
dbContentAsString, err := utils.LoadStringFromFile(
|
||||||
getFullPathToDatabase(name),
|
getFullPathToDatabase(nameOfDatabase),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Database{}, err
|
return Database{}, err
|
||||||
@ -324,30 +417,43 @@ func loadDatabase(name string) (Database, error) {
|
|||||||
return database, nil
|
return database, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func safeDatabase(name string, database Database) error {
|
func lockDatabase(nameOfDatabase string) {
|
||||||
|
waitForDatabaseToUnlock(nameOfDatabase)
|
||||||
|
|
||||||
|
databaseIsLocked[nameOfDatabase] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeDatabase(nameOfDatabase string, database Database) error {
|
||||||
databaseAsJSONBytes, err := json.MarshalIndent(database, "", "\t")
|
databaseAsJSONBytes, err := json.MarshalIndent(database, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("could not marshal database: " + name)
|
return errors.New("could not marshal database: " + nameOfDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = utils.WriteStringToFile(getFullPathToDatabase(name), string(databaseAsJSONBytes))
|
err = utils.WriteStringToFile(getFullPathToDatabase(nameOfDatabase), string(databaseAsJSONBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("could not write to file: " + getFullPathToDatabase(name))
|
return errors.New("could not write to file: " + getFullPathToDatabase(nameOfDatabase))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDeepCopyOfDatabase(name string) (Database, error) {
|
func unlockDatabase(nameOfDatabase string) {
|
||||||
originalDatabase, databaseExists := cluster[name]
|
databaseIsLocked[nameOfDatabase] = false
|
||||||
if !databaseExists {
|
}
|
||||||
return nil, errors.New("database does not exist")
|
|
||||||
}
|
func waitForDatabaseToUnlock(nameOfDatabase string) {
|
||||||
|
isLocked, databaseExists := databaseIsLocked[nameOfDatabase]
|
||||||
copyOfDatabase := make(Database)
|
if !databaseExists {
|
||||||
for key, value := range originalDatabase {
|
return
|
||||||
copyOfDatabase[key] = value
|
}
|
||||||
}
|
|
||||||
|
for {
|
||||||
return copyOfDatabase, nil
|
if !isLocked {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
|
||||||
|
isLocked = databaseIsLocked[nameOfDatabase]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user