确保MongoDB以动态时间间隔使数据过期,并且调用是幂等的

确保MongoDB以动态时间间隔使数据过期,并且调用是幂等的,mongodb,go,mgo,Mongodb,Go,Mgo,我使用MongoDB将用户生成的链接保存在存储器中。用户可以声明他们希望URL在过期之前保存多长时间。每个用户id也是唯一的 理想情况下,我希望我的请求是幂等的。我想打尽可能多的电话,而不必检查最后一次通话是否有过期值 下面的代码似乎给了我: “名为:creationtime_1的索引已存在,且具有不同的选项”或 索引不存在 这是我第一次使用MongoDB,我希望能有任何见解。我想我的代码可能也有多余的检查,但我不知道怎么做 ``` ```解决方案 方法:使用日期字段,将值设置为文档过期的日

我使用MongoDB将用户生成的链接保存在存储器中。用户可以声明他们希望URL在过期之前保存多长时间。每个用户id也是唯一的

理想情况下,我希望我的请求是幂等的。我想打尽可能多的电话,而不必检查最后一次通话是否有过期值

下面的代码似乎给了我:

  • “名为:creationtime_1的索引已存在,且具有不同的选项”或
  • 索引不存在
这是我第一次使用MongoDB,我希望能有任何见解。我想我的代码可能也有多余的检查,但我不知道怎么做

```

```

解决方案 方法:使用日期字段,将值设置为文档过期的日期,创建一个TTL索引,将
ExpireAfterSeconds
设置为0,MongoDB后台TTL清除过程将删除过期的文档

笔记 然而,在使用TTL指标时存在一些模糊性。由于为每个要过期的文档设置一个流程、等待过期时间然后删除文档的成本太高,MongoDB选择了不同的解决方案。因此,无法保证您的文档会在到期时立即过期,并且文档可能比设置的过期日期长不到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.")
}
解决方案 方法:使用日期字段,将值设置为文档过期的日期,创建一个TTL索引,将
ExpireAfterSeconds
设置为0,MongoDB后台TTL清除过程将删除过期的文档

笔记 然而,在使用TTL指标时存在一些模糊性。由于为每个要过期的文档设置一个流程、等待过期时间然后删除文档的成本太高,MongoDB选择了不同的解决方案。因此,无法保证您的文档会在到期时立即过期,并且文档可能比设置的过期日期长不到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确实支持TTL索引,它将在指定的时间段自动删除文档。请看,您只需创建一次索引,而不是每个添加的值创建一次。@MarkusWMahlberg,但每个值都有不同的过期期限。到目前为止,我正在为每个请求创建一个新索引,并在不检查错误的情况下使用相同的键删除以前的索引。您能推荐其他的吗?在任何给定的时间,您只能有一个具有给定名称的索引。您可以在数据上使用字段
expirationDate
,并在此expirationDate上将TTL索引设置为0。这样,单个文档将在所述到期日期后1分钟内到期。请注意,TTL清洗每分钟仅运行一次。我会准备一个例子,我想看看你的例子。请记住,每个传入请求都有一个URL,该URL必须在动态用户指定的时间过期。MongoDB确实支持TTL索引,它将在指定的时间段自动删除文档。请看,您只需创建一次索引,而不是每个添加的值创建一次。@MarkusWMahlberg,但每个值都有不同的过期期限。到目前为止,我正在为每个请求创建一个新索引,并在不检查错误的情况下使用相同的键删除以前的索引。您能推荐其他的吗?在任何给定的时间,您只能有一个具有给定名称的索引。您可以在数据上使用字段
expirationDate
,并在此expirationDate上将TTL索引设置为0。这样,单个文档将在所述到期日期后1分钟内到期。请注意,TTL清洗每分钟仅运行一次。我会准备一个例子,我想看看你的例子。请记住,每个传入请求都有一个URL,该URL必须在动态用户指定的时间过期。