在RavenDB中为文档生成唯一标识符的策略

时间:2014-04-07 04:59:50

标签: ravendb

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

因此,如果我打开今天的第一张票,我希望它是SES140407-01。下一个是SES140407-02,依此类推。短划线后不必是两位数。

如何确保以确保整个组织100%唯一的方式生成此内容(没有orgs具有相同的标记)?

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

是否有一种实用的方法可以确保我获得一个独特的票号,即使两个或更多人几乎同时报告一个新票号?

编辑:每个组织都是RavenDB中的文档,可以轻松保存LastIssuedTicketId等属性。我的挑战基本上是找到阅读这个领域的最佳方式,生成一个新领域,并以“竞争条件安全”的方式将其存储回来。

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

1 个答案:

答案 0 :(得分: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乐观并发保证了唯一性。因此每个序列都有自己的文档,我们在生成新的序列号时会更新。此外,如果多个线程在同一进程(appdomain)的同一时刻执行,则lock会减少额外的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);