Golang BoltDB 删除键似乎不起作用
Golang BoltDB Delete Key Seemingly Not Working
CentOS 7,Githubboltdb/bolt版本1.3.1,go版本go1.17.7linux/amd64
这个issue可能是对BoltDB工作原理的误解,也可能是我有bug,也可能是有问题。我以前用过 BoltDB,并且取得了很好的效果。虽然,我没有明确地寻找这个问题。我所看到的是,我尝试从存储桶中删除一个键,键及其值在活动 db.Update 中被删除,但在 db.Update 结束后它仍然存在。寻找可能发生的事情的任何解释。看来这个功能不可能被破坏。
我正在使用 BoltDB 存储桶来存储与用于创建新帐户的电子邮件地址关联的临时令牌。想要整洁并立即清理旧数据(过期的令牌、误用的令牌等)。很标准的东西。临时token的结构为(key为临时token,10位随机字符串):
(临时令牌是Bucket key)
type tempTokenStruct struct {
EmailAddress string `json:"emailaddress"` // Email Address to be changed
TokenExpiryTime int64 `json:"tokenexpirytime"` // Expiry Time for token in Epoch time
}
用户在 Web 表单中输入电子邮件地址并点击 'submit'。这会创建对 REST 服务的调用,该服务会在临时令牌 table 中创建一个条目,例如:
"BpLnfgDsc2" => foo@bar.com, 1645650084
该服务通过电子邮件发送 URL,其中嵌入了临时令牌,link 将用户带到一个表单,允许他们输入他们的电子邮件地址(再次验证)和新的密码(两次)。点击提交然后会导致从网络处理程序中调用以下代码:
func checkTokenValid(emailAddress string, tempToken string) error {
var tempTokenData tempTokenStruct
var tempTokenBytes []byte
tempTokenBytes = []byte(tempToken)
db, err := bolt.Open(USER_DB_NAME, 0644, nil)
if err != nil {
return err
}
defer db.Close()
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket := tx.Bucket([]byte("temptokens"))
// The bucket hasn't been created, so there are no stored tokens
if tempTokenBucket == nil {
return errors.New("Not Authorized (1): Please request a new password new/change email from the login page.")
}
// There is no matching token stored in the bucket, so this is an invalid request
tempTokenJSON := tempTokenBucket.Get(tempTokenBytes)
//[I've put a printf here: A]
if tempTokenJSON == nil {
return errors.New("Not Authorized (2): Please request a new password new/change email from the login page.")
}
jsonConvertErr := json.Unmarshal(tempTokenJSON, &tempTokenData)
if jsonConvertErr != nil {
tempTokenBucket.Delete(tempTokenBytes)
return errors.New("Not Authorized (3): Please request a new password new/change email from the login page.")
}
// Check to see if the time is expired, if so, remove the key and indicate error
if tempTokenData.TokenExpiryTime < time.Now().Unix() {
tempTokenBucket.Delete(tempTokenBytes)
//[I've put a printf here: B]
return errors.New("Not Authorized (4): Please request a new password new/change email from the login page.")
}
// Check to see if the email addresses match
if emailAddress != tempTokenData.EmailAddress {
tempTokenBucket.Delete(tempTokenBytes)
return errors.New("Not Authorized (5): Please request a new password new/change email from the login page.")
}
tempTokenBucket.Delete(tempTokenBytes)
return nil
})
// This is test code to see if the key was in fact deleted
db.Update(func(tx *bolt.Tx) error {
tempTokenBucket := tx.Bucket([]byte("temptokens"))
tempTokenJSON := tempTokenBucket.Get(tempTokenBytes)
// [I've put a printf here: C]
return nil
})
return err
}
我正在使用超时令牌 (4) 进行测试,所以我的想法是,当它遇到那个超时令牌时,它想要从存储桶中删除这个现在无效的令牌。
在A位置,打印:
首先获取呼叫令牌 BpLnfgDsc2 是 {"emailaddress":"foo@bar.com","tokenexpirytime":1645650084}
在B位置我放了一个代码,做了一个.Get,它打印出来(看起来被删除了):
DB Close前(4),删除后token BpLnfgDsc2为
在C位置,它打印(看起来是回来了):
数据库关闭后,令牌 BpLnfgDsc2 为 {"emailaddress":"foo@bar.com","tokenexpirytime":1645650084}
没有返回任何错误。我已经重复了很多次,将 fmt.Printfs 放在各处以查看发生了什么。结果是一样的,密钥似乎没有被删除。坐下后,我 'vi -b' 数据库文件,键,值仍然存在。 运行 坐下后,它仍然在那里看到键值。我很困惑,任何指点将不胜感激。
更新:Put/Get/Delete/Get 的基本螺栓功能按照此测试代码工作(应该很明显):
package main
import "fmt"
import "encoding/json"
import "github.com/boltdb/bolt"
type tempTokenStruct struct {
EmailAddress string `json:"emailaddress"` // Email Address to be changed (Temporary Token is the DB key)
TokenExpiryTime int64 `json:"tokenexpirytime"` // Expiry Time for token in Epoch time
}
func main() {
var tempToken tempTokenStruct
tempToken.EmailAddress = "foo@bar.com"
tempToken.TokenExpiryTime = 1234567890
tempTokenDataJSON, jsonMarshalError := json.Marshal(tempToken)
if jsonMarshalError != nil {
fmt.Printf("JSON Marshal Error: %s\n", jsonMarshalError.Error())
return
}
tempTokenKey := []byte("foo")
db, err := bolt.Open("test.db", 0644, nil)
if err != nil {
fmt.Printf("Error opening Database\n")
return
}
defer db.Close()
// Put a key in the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
dbPutError := tempTokenBucket.Put(tempTokenKey, []byte(tempTokenDataJSON))
return dbPutError
})
if err != nil {
fmt.Printf("Error putting key value pair into table: %s\n", err.Error())
}
// Check if the key/value is there after putting it in
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" just after putting it in there\n", tempTokenKey, valueGet)
return nil
})
// Delete that key from the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
dbDeleteError := tempTokenBucket.Delete(tempTokenKey)
return dbDeleteError
})
if err != nil {
fmt.Printf("Error Deleting key from bucket: %s\n", err.Error())
}
// Check if the key/value is there after deleting it
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" after the delete\n", tempTokenKey, valueGet)
return nil
})
if err != nil {
fmt.Printf("Error getting key from table: %s\n", err.Error())
}
}
打印出来:
令牌的价值:“foo”是“{“emailaddress”:“foo@bar.com”,“tokenexpirytime”:1234567890}“刚把它放在那里
令牌值:“foo”在删除后为“”
所以,不确定为什么其他代码不起作用。几乎就像删除使用不同的键一样,但键在其他代码中是相同的。
我认为 db.Update
与 non-nil return 值的行为是这里的混淆。根据 the docs
Inside the closure, you have a consistent view of the database. You commit the transaction by returning nil at the end. You can also rollback the transaction at any point by returning an error.
您 return 出错了:
return errors.New("Not Authorized (4): Please request a new password new/change email from the login page.")
这意味着 db.Update(
中的所有操作都被回滚。这可以在您的简单示例中复制,只需稍作更改 (return fmt.Errorf("RETURNING ERROR HERE")
):
package main
import "fmt"
import "encoding/json"
import "github.com/boltdb/bolt"
type tempTokenStruct struct {
EmailAddress string `json:"emailaddress"` // Email Address to be changed (Temporary Token is the DB key)
TokenExpiryTime int64 `json:"tokenexpirytime"` // Expiry Time for token in Epoch time
}
func main() {
var tempToken tempTokenStruct
tempToken.EmailAddress = "foo@bar.com"
tempToken.TokenExpiryTime = 1234567890
tempTokenDataJSON, jsonMarshalError := json.Marshal(tempToken)
if jsonMarshalError != nil {
fmt.Printf("JSON Marshal Error: %s\n", jsonMarshalError.Error())
return
}
tempTokenKey := []byte("foo")
db, err := bolt.Open("test.db", 0644, nil)
if err != nil {
fmt.Printf("Error opening Database\n")
return
}
defer db.Close()
// Put a key in the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
dbPutError := tempTokenBucket.Put(tempTokenKey, []byte(tempTokenDataJSON))
return dbPutError
})
if err != nil {
fmt.Printf("Error putting key value pair into table: %s\n", err.Error())
}
// Check if the key/value is there after putting it in
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" just after putting it in there\n", tempTokenKey, valueGet)
return nil
})
// Delete that key from the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
tempTokenBucket.Delete(tempTokenKey)
return fmt.Errorf("RETURNING ERROR HERE") // CHANGED HERE
})
if err != nil {
fmt.Printf("Error Deleting key from bucket: %s\n", err.Error())
}
// Check if the key/value is there after deleting it
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" after the delete\n", tempTokenKey, valueGet)
return nil
})
if err != nil {
fmt.Printf("Error getting key from table: %s\n", err.Error())
}
}
现在的输出是:
Value for Token: "foo" is "{"emailaddress":"foo@bar.com","tokenexpirytime":1234567890}" just after putting it in there
Error Deleting key from bucket: RETURNING ERROR HERE
Value for Token: "foo" is "{"emailaddress":"foo@bar.com","tokenexpirytime":1234567890}" after the delete
这似乎与您在主代码中看到的相符。修复相对简单 - 如果您想要提交更改,请不要 return 错误。
CentOS 7,Githubboltdb/bolt版本1.3.1,go版本go1.17.7linux/amd64
这个issue可能是对BoltDB工作原理的误解,也可能是我有bug,也可能是有问题。我以前用过 BoltDB,并且取得了很好的效果。虽然,我没有明确地寻找这个问题。我所看到的是,我尝试从存储桶中删除一个键,键及其值在活动 db.Update 中被删除,但在 db.Update 结束后它仍然存在。寻找可能发生的事情的任何解释。看来这个功能不可能被破坏。
我正在使用 BoltDB 存储桶来存储与用于创建新帐户的电子邮件地址关联的临时令牌。想要整洁并立即清理旧数据(过期的令牌、误用的令牌等)。很标准的东西。临时token的结构为(key为临时token,10位随机字符串):
(临时令牌是Bucket key)
type tempTokenStruct struct {
EmailAddress string `json:"emailaddress"` // Email Address to be changed
TokenExpiryTime int64 `json:"tokenexpirytime"` // Expiry Time for token in Epoch time
}
用户在 Web 表单中输入电子邮件地址并点击 'submit'。这会创建对 REST 服务的调用,该服务会在临时令牌 table 中创建一个条目,例如:
"BpLnfgDsc2" => foo@bar.com, 1645650084
该服务通过电子邮件发送 URL,其中嵌入了临时令牌,link 将用户带到一个表单,允许他们输入他们的电子邮件地址(再次验证)和新的密码(两次)。点击提交然后会导致从网络处理程序中调用以下代码:
func checkTokenValid(emailAddress string, tempToken string) error {
var tempTokenData tempTokenStruct
var tempTokenBytes []byte
tempTokenBytes = []byte(tempToken)
db, err := bolt.Open(USER_DB_NAME, 0644, nil)
if err != nil {
return err
}
defer db.Close()
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket := tx.Bucket([]byte("temptokens"))
// The bucket hasn't been created, so there are no stored tokens
if tempTokenBucket == nil {
return errors.New("Not Authorized (1): Please request a new password new/change email from the login page.")
}
// There is no matching token stored in the bucket, so this is an invalid request
tempTokenJSON := tempTokenBucket.Get(tempTokenBytes)
//[I've put a printf here: A]
if tempTokenJSON == nil {
return errors.New("Not Authorized (2): Please request a new password new/change email from the login page.")
}
jsonConvertErr := json.Unmarshal(tempTokenJSON, &tempTokenData)
if jsonConvertErr != nil {
tempTokenBucket.Delete(tempTokenBytes)
return errors.New("Not Authorized (3): Please request a new password new/change email from the login page.")
}
// Check to see if the time is expired, if so, remove the key and indicate error
if tempTokenData.TokenExpiryTime < time.Now().Unix() {
tempTokenBucket.Delete(tempTokenBytes)
//[I've put a printf here: B]
return errors.New("Not Authorized (4): Please request a new password new/change email from the login page.")
}
// Check to see if the email addresses match
if emailAddress != tempTokenData.EmailAddress {
tempTokenBucket.Delete(tempTokenBytes)
return errors.New("Not Authorized (5): Please request a new password new/change email from the login page.")
}
tempTokenBucket.Delete(tempTokenBytes)
return nil
})
// This is test code to see if the key was in fact deleted
db.Update(func(tx *bolt.Tx) error {
tempTokenBucket := tx.Bucket([]byte("temptokens"))
tempTokenJSON := tempTokenBucket.Get(tempTokenBytes)
// [I've put a printf here: C]
return nil
})
return err
}
我正在使用超时令牌 (4) 进行测试,所以我的想法是,当它遇到那个超时令牌时,它想要从存储桶中删除这个现在无效的令牌。
在A位置,打印: 首先获取呼叫令牌 BpLnfgDsc2 是 {"emailaddress":"foo@bar.com","tokenexpirytime":1645650084}
在B位置我放了一个代码,做了一个.Get,它打印出来(看起来被删除了): DB Close前(4),删除后token BpLnfgDsc2为
在C位置,它打印(看起来是回来了): 数据库关闭后,令牌 BpLnfgDsc2 为 {"emailaddress":"foo@bar.com","tokenexpirytime":1645650084}
没有返回任何错误。我已经重复了很多次,将 fmt.Printfs 放在各处以查看发生了什么。结果是一样的,密钥似乎没有被删除。坐下后,我 'vi -b' 数据库文件,键,值仍然存在。 运行 坐下后,它仍然在那里看到键值。我很困惑,任何指点将不胜感激。
更新:Put/Get/Delete/Get 的基本螺栓功能按照此测试代码工作(应该很明显):
package main
import "fmt"
import "encoding/json"
import "github.com/boltdb/bolt"
type tempTokenStruct struct {
EmailAddress string `json:"emailaddress"` // Email Address to be changed (Temporary Token is the DB key)
TokenExpiryTime int64 `json:"tokenexpirytime"` // Expiry Time for token in Epoch time
}
func main() {
var tempToken tempTokenStruct
tempToken.EmailAddress = "foo@bar.com"
tempToken.TokenExpiryTime = 1234567890
tempTokenDataJSON, jsonMarshalError := json.Marshal(tempToken)
if jsonMarshalError != nil {
fmt.Printf("JSON Marshal Error: %s\n", jsonMarshalError.Error())
return
}
tempTokenKey := []byte("foo")
db, err := bolt.Open("test.db", 0644, nil)
if err != nil {
fmt.Printf("Error opening Database\n")
return
}
defer db.Close()
// Put a key in the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
dbPutError := tempTokenBucket.Put(tempTokenKey, []byte(tempTokenDataJSON))
return dbPutError
})
if err != nil {
fmt.Printf("Error putting key value pair into table: %s\n", err.Error())
}
// Check if the key/value is there after putting it in
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" just after putting it in there\n", tempTokenKey, valueGet)
return nil
})
// Delete that key from the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
dbDeleteError := tempTokenBucket.Delete(tempTokenKey)
return dbDeleteError
})
if err != nil {
fmt.Printf("Error Deleting key from bucket: %s\n", err.Error())
}
// Check if the key/value is there after deleting it
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" after the delete\n", tempTokenKey, valueGet)
return nil
})
if err != nil {
fmt.Printf("Error getting key from table: %s\n", err.Error())
}
}
打印出来:
令牌的价值:“foo”是“{“emailaddress”:“foo@bar.com”,“tokenexpirytime”:1234567890}“刚把它放在那里
令牌值:“foo”在删除后为“”
所以,不确定为什么其他代码不起作用。几乎就像删除使用不同的键一样,但键在其他代码中是相同的。
我认为 db.Update
与 non-nil return 值的行为是这里的混淆。根据 the docs
Inside the closure, you have a consistent view of the database. You commit the transaction by returning nil at the end. You can also rollback the transaction at any point by returning an error.
您 return 出错了:
return errors.New("Not Authorized (4): Please request a new password new/change email from the login page.")
这意味着 db.Update(
中的所有操作都被回滚。这可以在您的简单示例中复制,只需稍作更改 (return fmt.Errorf("RETURNING ERROR HERE")
):
package main
import "fmt"
import "encoding/json"
import "github.com/boltdb/bolt"
type tempTokenStruct struct {
EmailAddress string `json:"emailaddress"` // Email Address to be changed (Temporary Token is the DB key)
TokenExpiryTime int64 `json:"tokenexpirytime"` // Expiry Time for token in Epoch time
}
func main() {
var tempToken tempTokenStruct
tempToken.EmailAddress = "foo@bar.com"
tempToken.TokenExpiryTime = 1234567890
tempTokenDataJSON, jsonMarshalError := json.Marshal(tempToken)
if jsonMarshalError != nil {
fmt.Printf("JSON Marshal Error: %s\n", jsonMarshalError.Error())
return
}
tempTokenKey := []byte("foo")
db, err := bolt.Open("test.db", 0644, nil)
if err != nil {
fmt.Printf("Error opening Database\n")
return
}
defer db.Close()
// Put a key in the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
dbPutError := tempTokenBucket.Put(tempTokenKey, []byte(tempTokenDataJSON))
return dbPutError
})
if err != nil {
fmt.Printf("Error putting key value pair into table: %s\n", err.Error())
}
// Check if the key/value is there after putting it in
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" just after putting it in there\n", tempTokenKey, valueGet)
return nil
})
// Delete that key from the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
tempTokenBucket.Delete(tempTokenKey)
return fmt.Errorf("RETURNING ERROR HERE") // CHANGED HERE
})
if err != nil {
fmt.Printf("Error Deleting key from bucket: %s\n", err.Error())
}
// Check if the key/value is there after deleting it
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" after the delete\n", tempTokenKey, valueGet)
return nil
})
if err != nil {
fmt.Printf("Error getting key from table: %s\n", err.Error())
}
}
现在的输出是:
Value for Token: "foo" is "{"emailaddress":"foo@bar.com","tokenexpirytime":1234567890}" just after putting it in there
Error Deleting key from bucket: RETURNING ERROR HERE
Value for Token: "foo" is "{"emailaddress":"foo@bar.com","tokenexpirytime":1234567890}" after the delete
这似乎与您在主代码中看到的相符。修复相对简单 - 如果您想要提交更改,请不要 return 错误。