假设我有类似支持票证系统(简化为例子)。它有许多用户和组织。每个用户可以是几个组织的成员,但典型的情况是一个org =>许多用户,其中大多数只属于这个组织。每个组织都有一个“标签”,用于为该组织构建“票号”。假设我们有一个名为StackExchange的组织需要标签SES。
因此,如果我打开今天的第一张票,我希望它是SES140407-01
。下一个是SES140407-02
,依此类推。短划线后不必是两位数。
如何确保以确保整个组织100%唯一的方式生成此内容(没有orgs具有相同的标记)?
注意:这不一定是数据库中的文档ID - 可能只是Guid或类似的。这只是一个票证参考 - 有点像一个slug - 会出现在相关的电子邮件等中。所以它必须是独一无二的,如果我们不“浪费”连续的案例编号hilo风格,我宁愿这样做。
是否有一种实用的方法可以确保我获得一个独特的票号,即使两个或更多人几乎同时报告一个新票号?
编辑:每个组织都是RavenDB
中的文档,可以轻松保存LastIssuedTicketId
等属性。我的挑战基本上是找到阅读这个领域的最佳方式,生成一个新领域,并以“竞争条件安全”的方式将其存储回来。
另一个编辑:要清楚 - 我打算在自己的软件中生成故障单ID。我正在寻找的是一种方式来询问RavenDB“最后一个票号是什么”,然后当我在那之后生成下一个票号时,“我是唯一使用它的人吗?” - 这样我就给我的票证一个唯一的案例ID,不一定与RavenDB认为文档ID有关。
答案 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);