0

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

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

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

(临时Token是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 表单中输入电子邮件地址并点击“提交”。这会创建对 REST 服务的调用,该服务会在临时令牌表中创建一个条目,例如:

"BpLnfgDsc2" => foo@bar.com, 1645650084

该服务通过电子邮件发送一个嵌入了临时令牌的 URL,该链接将用户带到一个表单,允许他们输入他们的电子邮件地址(再次验证)和新密码(两次)。点击提交然后会导致从 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 位置,它打印: First Get call token BpLnfgDsc2 is {"emailaddress":"foo@bar.com","tokenexpirytime":1645650084}

在 B 位置,我将执行 .Get 的代码放入其中,它会打印出来(看起来要被删除): DB Close (4) 之前,删除之后,令牌 BpLnfgDsc2 是

在 C 位置,它打印(看起来又回来了): DB Close 后,令牌 BpLnfgDsc2 是 {"emailaddress":"foo@bar.com","tokenexpirytime":1645650084}

没有任何错误返回。我已经重复了很多次,把 fmt.Printfs 放在任何地方,看看发生了什么。结果是一样的,密钥似乎没有被删除。在此之后,我'vi -b' DB 文件,键值仍然存在。在它坐下之后运行,它仍然可以看到那里的关键值。我很困惑,任何指针将不胜感激。

更新: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())
    }
}

打印出来:

Token 的值:"foo" 是 "{"emailaddress":"foo@bar.com","tokenexpirytime":1234567890}" 刚刚把它放在那里

Token 的值:删除后的“foo”为“”

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

4

1 回答 1

0

我相信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("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

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

于 2022-02-24T04:45:53.757 回答