Golang BoltDB 删除键似乎不起作用

发布于 2025-01-09 13:35:24 字数 6884 浏览 0 评论 0原文

CentOS 7,Github Boltdb/bolt 版本 1.3.1,go 版本 go1.17.7 linux/amd64

这个问题可能是对 BoltDB 工作原理的误解,或者可能是我有 bug,或者可能是有问题。我以前使用过BoltDB,并且取得了很好的效果。不过,我并没有明确寻找这个问题。我看到的是,我尝试从存储桶中删除一个键,并且该键及其值在活动 db.Update 中被删除,但在 db.Update 结束后它仍然存在。寻找可能发生的事情的任何解释。看来这个功能不可能被破坏。

我正在使用 BoltDB 存储桶来存储与电子邮件地址关联的临时令牌,以创建新帐户。想要立即整洁并清理旧数据(过期的令牌、滥用的令牌等)。相当标准的东西。临时令牌的结构是(密钥是临时令牌,10 位随机字符串):(

临时令牌是存储桶密钥)

type tempTokenStruct struct {

EmailAddress        string   `json:"emailaddress"`        // Email Address to be changed 

TokenExpiryTime     int64    `json:"tokenexpirytime"`     // Expiry Time for token in Epoch time

}

用户在 Web 表单中输入电子邮件地址并点击“提交”。这将创建对 REST 服务的调用,该服务会在临时令牌表中创建一个条目,例如:

“BpLnfgDsc2”=> [电子邮件受保护],1645650084

该服务通过电子邮件发送包含临时令牌的网址嵌入的,该链接将用户带到一个表单,允许他们输入电子邮件地址(再次验证)和新密码(两次)。点击“提交”后,会从 Web 处理程序中调用以下代码:

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":"[电子邮件受保护] “,”令牌到期时间“:1645650084}

在 B 位置,我放入执行 .Get 的代码,它打印出来(看起来已被删除): 在DB Close(4)之前,删除之后,token BpLnfgDsc2

在C位置,它打印(看起来又回来了): DB 关闭后,令牌 BpLnfgDsc2 为 {"emailaddress":"[电子邮件受保护]","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 = "[email protected]"
    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”:”[email protected]","tokenexpirytime":1234567890}" 就在将其放入

令牌值之后:删除后“foo”是“”

所以,不知道为什么其他代码不起作用。几乎就像删除使用不同的键一样,但该键在其他代码中是相同的。

CentOS 7, Github boltdb/bolt version 1.3.1, go version go1.17.7 linux/amd64

This issue may go to a misunderstanding of how BoltDB works, or maybe I have a bug, or maybe there is an issue. I've used BoltDB before, and have had very good results. Though, I didn't explicly look for this issue. What I'm seeing is that I try to delete a key from a bucket, and the key and its value are deleted in the active db.Update, but it's still there after that db.Update is ended. Looking for any explanation of what might be going on. Seems like this functionality couldn't possibly be broken.

I am using a BoltDB bucket for storing a temporary token associated with an email address for creating a new account. Want to be tidy and clean up old data right away (expired tokens, misused tokens, etc). Pretty standard stuff. The structure for the temporary token is (the key is the temporary token, a 10 digit random character string):

(Temporary Token is the 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

}

The user enters an email address in a web form and hits 'submit'. That creates a call to the REST service that creates an entry in the temporary token table, like:

"BpLnfgDsc2" => [email protected], 1645650084

The service emails a URL that has the temporary token embedded, and that link takes the user to a form that allows them to put in their email address (again to verify) and new password (twice). Hitting Submit then results in the following code being called from within a web handler:

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
}

I'm testing with a timed-out token (4), so the idea is that when it encounters that timed out token, it wants to delete this now invalid token from the bucket.

At the A location, it prints:
First Get call token BpLnfgDsc2 is {"emailaddress":"[email protected]","tokenexpirytime":1645650084}

At the B location I put code in that does a .Get, it prints out (looks to be deleted):
Before the DB Close (4), after deleting, token BpLnfgDsc2 is

At the C location, it prints (looks to be back):
After the DB Close, token BpLnfgDsc2 is {"emailaddress":"[email protected]","tokenexpirytime":1645650084}

There are no errors returned for anything. I've repeated this many times, putting fmt.Printfs everywhere to see what's going on. The results are the same, the key doesn't seem to be getting deleted. After this sits, I 'vi -b' the DB file, and the key, value is still there. Running after it sits, it still sees the key value there. I'm confused, and any pointers will be appreciated.

Update: The basic bolt functionality of Put/Get/Delete/Get works as per this test code (should be obvious):

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 = "[email protected]"
    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())
    }
}

Prints out:

Value for Token: "foo" is "{"emailaddress":"[email protected]","tokenexpirytime":1234567890}" just after putting it in there

Value for Token: "foo" is "" after the delete

So, not sure why the other code doesn't work. Almost as if the delete is using a different key, but the key is the same across the other code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

GRAY°灰色天空 2025-01-16 13:35:24

我相信具有非零返回值的 db.Update 的行为是这里的混乱。根据文档

在闭包内,您可以看到一致的数据库视图。您通过在最后返回 nil 来提交交易。您还可以随时通过返回错误来回滚事务。

您返回的错误是:

return errors.New("Not Authorized (4): Please request a new password new/change email from the login page.")

这意味着 db.Update( 中的所有操作都将回滚。这可以在您的简单示例中进行一些小的更改来复制 (return fmt.Errorf( “在此处返回错误”)):

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 = "[email protected]"
    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":"[email protected]","tokenexpirytime":1234567890}" just after putting it in there
Error Deleting key from bucket: RETURNING ERROR HERE
Value for Token: "foo" is "{"emailaddress":"[email protected]","tokenexpirytime":1234567890}" after the delete

这似乎与您在主代码中看到的内容相匹配。修复相对简单 - 如果您希望提交更改,则不要返回错误。

I believe that the behaviour of db.Update with a non-nil return value is the confusion here. As per 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.

You are returning an error with:

return errors.New("Not Authorized (4): Please request a new password new/change email from the login page.")

This means that all operations within that db.Update( are rolled back. This can be replicated in your simple example with a small change (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 = "[email protected]"
    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())
    }
}

The output is now:

Value for Token: "foo" is "{"emailaddress":"[email protected]","tokenexpirytime":1234567890}" just after putting it in there
Error Deleting key from bucket: RETURNING ERROR HERE
Value for Token: "foo" is "{"emailaddress":"[email protected]","tokenexpirytime":1234567890}" after the delete

This appears to match what you are seeing in your main code. The fix is relatively simple - don't return an error if you want changes to be committed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文