【问题标题】:Strategy for generating unique identifier for document in RavenDB在 RavenDB 中为文档生成唯一标识符的策略
【发布时间】:2014-04-09 21:25:41
【问题描述】:

假设我有类似支持票证系统的东西(简化为示例)。它有许多用户和组织。每个用户可以是多个组织的成员,但典型的情况是一个组织 => 多个用户,并且大多数用户只属于这个组织。每个组织都有一个“标签”,用于构造该组织的“票号”。假设我们有一个名为 StackExchange 的组织需要标签 SES。

所以如果我打开今天的第一张票,我希望它是SES140407-01。接下来是SES140407-02 等等。破折号后不必是两位数。

我如何确保它以确保它在整个组织中 100% 唯一的方式生成(没有组织将具有相同的标签)?

注意:这不一定是数据库中的文档 ID - 这可能只是一个 Guid 或类似的。这只是一个票证参考——有点像一个蛞蝓——将出现在相关的电子邮件等中。所以它必须是唯一的,如果我们不“浪费”hilo 风格的连续案例编号,我会更喜欢。

有没有一种实用的方法可以确保我得到一个唯一的票号,即使两个或更多人几乎同时报告了一个新票号?

编辑:每个组织都是RavenDB 中的一个文档,并且可以轻松保存LastIssuedTicketId 之类的属性。我的挑战基本上是找到读取该字段的最佳方法,生成一个新字段,然后以“竞争条件安全”的方式将其存储回来。

另一个编辑:要清楚 - 我打算在我自己的软件中生成票证 ID。我正在寻找一种方法来询问 RavenDB“最后一个票号是什么”,然后当我生成下一个票号时,“我是唯一一个使用这个的人吗?” - 所以我给我的票一个唯一的案例 ID,不一定与 RavenDB 认为的文档 ID 相关。

【问题讨论】:

    标签: ravendb


    【解决方案1】:

    我使用为 RavenDB 编写的通用序列生成器:

    public class SequenceGenerator
    {
      private static readonly object Lock = new object();
      private readonly IDocumentStore _docStore;
    
      public SequenceGenerator(IDocumentStore docStore)
      {
        _docStore = docStore;
      }
    
      public int GetNextSequenceNumber(string sequenceKey)
      {
        lock (Lock)
        {
          using (new TransactionScope(TransactionScopeOption.Suppress))
          {
            while (true)
            {
              try
              {
                var document = GetDocument(sequenceKey);
                if (document == null)
                {
                  PutDocument(new JsonDocument
                  {
                    Etag = Etag.Empty,
                    // sending empty guid means - ensure the that the document does NOT exists
                    Metadata = new RavenJObject(),
                    DataAsJson = RavenJObject.FromObject(new { Current = 0 }),
                    Key = sequenceKey
                  });
                  return 0;
                }
    
                var current = document.DataAsJson.Value<int>("Current");
                current++;
    
                document.DataAsJson["Current"] = current;
                PutDocument(document);
    
                {
                  return current;
                }
              }
              catch (ConcurrencyException)
              {
                // expected, we need to retry
              }
            }
          }
        }
      }
    
      private void PutDocument(JsonDocument document)
      {
        _docStore.DatabaseCommands.Put(
          document.Key,
          document.Etag,
          document.DataAsJson,
          document.Metadata);
      }
    
      private JsonDocument GetDocument(string key)
      {
        return _docStore.DatabaseCommands.Get(key);
      }
    }
    

    它根据sequenceKey 生成增量唯一序列。基于 Etag 的 raven 乐观并发保证了唯一性。所以每个序列都有自己的文档,我们会在生成新的序列号时更新这些文档。此外,lock 如果多个线程同时在同一个进程(appdomain)上执行,它会减少额外的 db 调用。

    对于您的情况,您可以这样使用它:

    var sequenceKey = string.Format("{0}{1:yyMMdd}", yourCompanyPrefix, DateTime.Now);
    var nextSequenceNumber = new SequenceGenerator(yourDocStore).GetNextSequenceNumber(sequenceKey);
    var nextSequenceKey = string.Format("{0}-{1:00}", sequenceKey, nextSequenceNumber);
    

    【讨论】:

    • 谢谢!这对我来说似乎很有意义,所以我会尝试一下。 :)
    • 我没有尝试在高级场景中使用它(服务器复制/分片等,多个 Web 服务器与单个 RavenDB 服务器),但经过相当多的测试证明它可以正常工作。我可能需要找到一种方法来清理存储在数据库中的文档,但我怀疑这将是一件大事。再次感谢!
    猜你喜欢
    • 2017-10-20
    • 1970-01-01
    • 1970-01-01
    • 2011-08-06
    • 1970-01-01
    • 2014-05-28
    • 2010-09-16
    • 1970-01-01
    相关资源
    最近更新 更多