确保 MongoDB 以动态时间间隔使数据过期并且调用是幂等的
Ensure MongoDB expires data at dynamic time intervals and calls are idempotent
我正在使用 MongoDB 将用户生成的链接保存在存储中。用户可以说明他们希望 URL 在 expired.Every 用户 ID 也是唯一的之前保存多长时间。
理想情况下,我希望我的请求是幂等的。我想拨打尽可能多的电话,而不必检查最后一次电话是否有到期值。
我下面的代码似乎给了我:
- "Index with name: creationtime_1 already exists with different options" 或
- 索引不存在。
这是我第一次 运行 使用 MongoDB,如果 insights.I 认为我可能对我的代码也进行了冗余检查,我将不胜感激,但我不知道还有什么办法去做
```
//mongo settings
sessionTTL := mgo.Index{
Key: []string{"creationtime"},
Unique: false,
DropDups: false,
Background: true,
ExpireAfter: time.Hour * time.Duration(expires)} // Expire in expire time
// START MONGODB
session, err := mgo.Dial(tokenMongo)
if err != nil {
return "", err
}
defer session.Close()
//session.SetSafe(&mgo.Safe{})
// Optional. Switch the session to a monotonic behavior.
id := uuid.NewV4().String()
thistime := time.Now().Local()
// find index
err = session.DB("tokenresults").C("tokenurl").Find(bson.M{"id": id}).One(&result)
if err == nil{
//Drop old values if exist // cant drop if empty
if err := session.DB("tokenresults").C("tokenurl").DropIndex("creationtime"); err != nil {
return "", err
}
}
//add stuff
c := session.DB("tokenresults").C("tokenurl")
err = c.Insert(&TokenUrl{id, tokenstring, thistime}, )
if err != nil {
return "", err
}
// create index //add session ttl // cant create if exists
if err := session.DB("tokenresults").C("tokenurl").EnsureIndex(sessionTTL); err != nil {
return "", err
}
```
解决方案
方法is documented:使用日期字段,将值设置为文档过期的日期,创建一个ExpireAfterSeconds
设置为0和MongoDB背景的TTL-Index TTL 清除过程将删除过期的文档。
备注
但是,在使用 TTL 索引时存在一些模糊性。由于对每个即将过期的文档都有一个过程,等待过期时间,然后删除文档,成本太高,MongoDB选择了不同的解决方案。 There is a background process which checks for expired documents once a minute。因此,无法保证您的文件会在到期时间立即到期,并且文件可能比设定的到期日期长不到 2 分钟(由于过载或其他原因而缺少第一个 运行,并且只是在接下来的 运行 中删除)。但是请注意,这仅在非常特殊的情况下才会发生。通常,您的文件会在过期的一分钟内被删除。
说明
我们基本上在这里做的是添加一个字段 ExpirationDate
并创建一个 TTL 索引,该索引设置为检查此到期日期。 ExpirationDate
设置为哪个值完全取决于您。使用工厂模式生成会话或其他内容。
请注意,下面的代码中解释了一些注意事项。
package main
import (
"flag"
"fmt"
"log"
"time"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
const (
// SESSION_TIMEOUT is a fixed and relatively short
// timeout for demo purposes
SESSION_TIMEOUT = 1 * time.Minute
)
// Session is just a sample session struct
// with various session related data and the
// date on which a session should expire.
type Session struct {
ID bson.ObjectId `bson:"_id"`
User string
Foo string
Bar string
ExpirationDate time.Time `bson:"expirationDate"`
}
// NewSession is just a simple helper method to
// return a session with a properly set expiration time
func NewSession(user, foo, bar string) Session {
// We use a static timeout here.
// However, you can easily adapt this to use an arbitrary timeout.
return Session{
ID: bson.NewObjectId(),
User: user,
Foo: foo,
Bar: bar,
ExpirationDate: time.Now().Add(SESSION_TIMEOUT),
}
}
var (
mgohost string
mgoport int
db string
col string
)
func init() {
flag.StringVar(&mgohost, "host", "localhost", "MongoDB host")
flag.IntVar(&mgoport, "port", 27017, "MongoDB port")
flag.StringVar(&db, "db", "test", "MongoDB database")
flag.StringVar(&col, "collection", "ttltest", "MongoDB collection")
}
func main() {
flag.Parse()
c, err := mgo.Dial(fmt.Sprintf("mongodb://%s:%d/%s", mgohost, mgoport, db))
if err != nil {
log.Fatalf("Error connecting to '%s:%d/%s': %s", mgohost, mgoport, db, err)
}
// We use a goroutine here in order to make sure
// that even when EnsureIndex blocks, our program continues
go func() {
log.Println("Ensuring sessionTTL index in background")
// Request a conncetion from the pool
m := c.DB(db).Session.Copy()
defer m.Close()
// We need to set this to 1 as 0 would fail to create the TTL index.
// See https://github.com/go-mgo/mgo/issues/103 for details
// This will expire the session within the minute after ExpirationDate.
//
// The TTL purging is done once a minute only.
// See https://docs.mongodb.com/manual/core/index-ttl/#timing-of-the-delete-operation
// for details
m.DB(db).C(col).EnsureIndex(mgo.Index{ExpireAfter: 1 * time.Second, Key: []string{"expirationDate"}})
log.Println("sessionTTL index is ready")
}()
s := NewSession("mwmahlberg", "foo", "bar")
if err := c.DB(db).C(col).Insert(&s); err != nil {
log.Fatalf("Error inserting %#v into %s.%s: %s", s, db, col, err)
}
l := Session{}
if err := c.DB(db).C(col).Find(nil).One(&l); err != nil {
log.Fatalf("Could not load session from %s.%s: %s", db, col, err)
}
log.Printf("Session with ID %s loaded for user '%s' which will expire in %s", l.ID, l.User, time.Until(l.ExpirationDate))
time.Sleep(2 * time.Minute)
// Let's check if the session is still there
if n, err := c.DB(db).C(col).Count(); err != nil {
log.Fatalf("Error counting documents in %s.%s: %s", db, col, err)
} else if n > 1 {
log.Fatalf("Uups! Someting went wrong!")
}
log.Println("All sessions were expired.")
}
我正在使用 MongoDB 将用户生成的链接保存在存储中。用户可以说明他们希望 URL 在 expired.Every 用户 ID 也是唯一的之前保存多长时间。
理想情况下,我希望我的请求是幂等的。我想拨打尽可能多的电话,而不必检查最后一次电话是否有到期值。
我下面的代码似乎给了我:
- "Index with name: creationtime_1 already exists with different options" 或
- 索引不存在。
这是我第一次 运行 使用 MongoDB,如果 insights.I 认为我可能对我的代码也进行了冗余检查,我将不胜感激,但我不知道还有什么办法去做
```
//mongo settings
sessionTTL := mgo.Index{
Key: []string{"creationtime"},
Unique: false,
DropDups: false,
Background: true,
ExpireAfter: time.Hour * time.Duration(expires)} // Expire in expire time
// START MONGODB
session, err := mgo.Dial(tokenMongo)
if err != nil {
return "", err
}
defer session.Close()
//session.SetSafe(&mgo.Safe{})
// Optional. Switch the session to a monotonic behavior.
id := uuid.NewV4().String()
thistime := time.Now().Local()
// find index
err = session.DB("tokenresults").C("tokenurl").Find(bson.M{"id": id}).One(&result)
if err == nil{
//Drop old values if exist // cant drop if empty
if err := session.DB("tokenresults").C("tokenurl").DropIndex("creationtime"); err != nil {
return "", err
}
}
//add stuff
c := session.DB("tokenresults").C("tokenurl")
err = c.Insert(&TokenUrl{id, tokenstring, thistime}, )
if err != nil {
return "", err
}
// create index //add session ttl // cant create if exists
if err := session.DB("tokenresults").C("tokenurl").EnsureIndex(sessionTTL); err != nil {
return "", err
}
```
解决方案
方法is documented:使用日期字段,将值设置为文档过期的日期,创建一个ExpireAfterSeconds
设置为0和MongoDB背景的TTL-Index TTL 清除过程将删除过期的文档。
备注
但是,在使用 TTL 索引时存在一些模糊性。由于对每个即将过期的文档都有一个过程,等待过期时间,然后删除文档,成本太高,MongoDB选择了不同的解决方案。 There is a background process which checks for expired documents once a minute。因此,无法保证您的文件会在到期时间立即到期,并且文件可能比设定的到期日期长不到 2 分钟(由于过载或其他原因而缺少第一个 运行,并且只是在接下来的 运行 中删除)。但是请注意,这仅在非常特殊的情况下才会发生。通常,您的文件会在过期的一分钟内被删除。
说明
我们基本上在这里做的是添加一个字段 ExpirationDate
并创建一个 TTL 索引,该索引设置为检查此到期日期。 ExpirationDate
设置为哪个值完全取决于您。使用工厂模式生成会话或其他内容。
请注意,下面的代码中解释了一些注意事项。
package main
import (
"flag"
"fmt"
"log"
"time"
mgo "gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
const (
// SESSION_TIMEOUT is a fixed and relatively short
// timeout for demo purposes
SESSION_TIMEOUT = 1 * time.Minute
)
// Session is just a sample session struct
// with various session related data and the
// date on which a session should expire.
type Session struct {
ID bson.ObjectId `bson:"_id"`
User string
Foo string
Bar string
ExpirationDate time.Time `bson:"expirationDate"`
}
// NewSession is just a simple helper method to
// return a session with a properly set expiration time
func NewSession(user, foo, bar string) Session {
// We use a static timeout here.
// However, you can easily adapt this to use an arbitrary timeout.
return Session{
ID: bson.NewObjectId(),
User: user,
Foo: foo,
Bar: bar,
ExpirationDate: time.Now().Add(SESSION_TIMEOUT),
}
}
var (
mgohost string
mgoport int
db string
col string
)
func init() {
flag.StringVar(&mgohost, "host", "localhost", "MongoDB host")
flag.IntVar(&mgoport, "port", 27017, "MongoDB port")
flag.StringVar(&db, "db", "test", "MongoDB database")
flag.StringVar(&col, "collection", "ttltest", "MongoDB collection")
}
func main() {
flag.Parse()
c, err := mgo.Dial(fmt.Sprintf("mongodb://%s:%d/%s", mgohost, mgoport, db))
if err != nil {
log.Fatalf("Error connecting to '%s:%d/%s': %s", mgohost, mgoport, db, err)
}
// We use a goroutine here in order to make sure
// that even when EnsureIndex blocks, our program continues
go func() {
log.Println("Ensuring sessionTTL index in background")
// Request a conncetion from the pool
m := c.DB(db).Session.Copy()
defer m.Close()
// We need to set this to 1 as 0 would fail to create the TTL index.
// See https://github.com/go-mgo/mgo/issues/103 for details
// This will expire the session within the minute after ExpirationDate.
//
// The TTL purging is done once a minute only.
// See https://docs.mongodb.com/manual/core/index-ttl/#timing-of-the-delete-operation
// for details
m.DB(db).C(col).EnsureIndex(mgo.Index{ExpireAfter: 1 * time.Second, Key: []string{"expirationDate"}})
log.Println("sessionTTL index is ready")
}()
s := NewSession("mwmahlberg", "foo", "bar")
if err := c.DB(db).C(col).Insert(&s); err != nil {
log.Fatalf("Error inserting %#v into %s.%s: %s", s, db, col, err)
}
l := Session{}
if err := c.DB(db).C(col).Find(nil).One(&l); err != nil {
log.Fatalf("Could not load session from %s.%s: %s", db, col, err)
}
log.Printf("Session with ID %s loaded for user '%s' which will expire in %s", l.ID, l.User, time.Until(l.ExpirationDate))
time.Sleep(2 * time.Minute)
// Let's check if the session is still there
if n, err := c.DB(db).C(col).Count(); err != nil {
log.Fatalf("Error counting documents in %s.%s: %s", db, col, err)
} else if n > 1 {
log.Fatalf("Uups! Someting went wrong!")
}
log.Println("All sessions were expired.")
}