创建一个序列——SetOnInsert 似乎什么都不做
Creating a sequence -- SetOnInsert appears to do nothing
我在尝试时遇到了问题,归根结底是,增加文档中的字段 或 插入整个文档。上下文是“尝试为序列插入初始文档或增加现有序列的序列号”。
此代码:
private async Task<int> GetSequenceNumber(string sequenceName)
{
var filter = new ExpressionFilterDefinition<Sequence>(x => x.Id == sequenceName);
var builder = Builders<Sequence>.Update;
var update = builder
.SetOnInsert(x => x.CurrentValue, 1000)
.Inc(x => x.CurrentValue, 1);
var sequence = await _context.SequenceNumbers.FindOneAndUpdateAsync(
filter,
update,
new FindOneAndUpdateOptions<Sequence>
{
IsUpsert = true,
ReturnDocument = ReturnDocument.After,
});
return sequence.CurrentValue;
}
导致异常
MongoDB.Driver.MongoCommandException: Command findAndModify failed: Updating the path 'currentvalue' would create a conflict at 'currentvalue'.
at MongoDB.Driver.Core.WireProtocol.CommandUsingCommandMessageWireProtocol`1.ProcessResponse(ConnectionId connectionId, CommandMessage responseMessage)
删除 SetOnInsert
不会导致错误,但会插入 currentValue 等于 1 而不是预期的 1000 的文档。
如果 SetOnInsert
没有被兑现,它几乎会出现,并且发生的事情是插入默认文档 然后 currentValue 在创建新文档时通过 Inc
自动递增。
我该如何克服这些问题?非 C# 解决方案也将受到欢迎,因为我可以翻译它...
好的,感谢评论中的@dododo,我现在意识到 Inc
和 SetOnInsert
不能同时应用。这是不直观的,因为您认为前者仅适用于更新而后者仅适用于插入。
我采用了下面的解决方案,它遭受了不止一个 round-trip,但至少有效,并且似乎适用于我基于并发的测试。
public async Task<int> GetSequenceNumber(string sequenceName, int tryCount)
{
if (tryCount > 5) throw new InvalidOperationException();
var filter = new ExpressionFilterDefinition<Sequence>(x => x.Id == sequenceName);
var builder = Builders<Sequence>.Update;
// optimistically assume value was already initialized
var update = builder.Inc(x => x.CurrentValue, 1);
var sequence = await _context.SequenceNumbers.FindOneAndUpdateAsync(
filter,
update,
new FindOneAndUpdateOptions<Sequence>
{
IsUpsert = true,
ReturnDocument = ReturnDocument.After,
});
if (sequence == null)
try
{
// we have to try to save a new sequence...
sequence = new Sequence { Id = sequenceName, CurrentValue = 1001 };
await _context.SequenceNumbers.InsertOneAsync(sequence);
}
// ...but something else could beat us to it
catch (MongoWriteException e) when (e.WriteError.Code == DuplicateKeyCode)
{
// ...so we have to retry an update
return await GetSequenceNumber(sequenceName, tryCount + 1);
}
return sequence.CurrentValue;
}
我相信还有其他选择。例如,可以使用聚合管道。
我在尝试时遇到了问题,归根结底是,增加文档中的字段 或 插入整个文档。上下文是“尝试为序列插入初始文档或增加现有序列的序列号”。
此代码:
private async Task<int> GetSequenceNumber(string sequenceName)
{
var filter = new ExpressionFilterDefinition<Sequence>(x => x.Id == sequenceName);
var builder = Builders<Sequence>.Update;
var update = builder
.SetOnInsert(x => x.CurrentValue, 1000)
.Inc(x => x.CurrentValue, 1);
var sequence = await _context.SequenceNumbers.FindOneAndUpdateAsync(
filter,
update,
new FindOneAndUpdateOptions<Sequence>
{
IsUpsert = true,
ReturnDocument = ReturnDocument.After,
});
return sequence.CurrentValue;
}
导致异常
MongoDB.Driver.MongoCommandException: Command findAndModify failed: Updating the path 'currentvalue' would create a conflict at 'currentvalue'. at MongoDB.Driver.Core.WireProtocol.CommandUsingCommandMessageWireProtocol`1.ProcessResponse(ConnectionId connectionId, CommandMessage responseMessage)
删除 SetOnInsert
不会导致错误,但会插入 currentValue 等于 1 而不是预期的 1000 的文档。
如果 SetOnInsert
没有被兑现,它几乎会出现,并且发生的事情是插入默认文档 然后 currentValue 在创建新文档时通过 Inc
自动递增。
我该如何克服这些问题?非 C# 解决方案也将受到欢迎,因为我可以翻译它...
好的,感谢评论中的@dododo,我现在意识到 Inc
和 SetOnInsert
不能同时应用。这是不直观的,因为您认为前者仅适用于更新而后者仅适用于插入。
我采用了下面的解决方案,它遭受了不止一个 round-trip,但至少有效,并且似乎适用于我基于并发的测试。
public async Task<int> GetSequenceNumber(string sequenceName, int tryCount)
{
if (tryCount > 5) throw new InvalidOperationException();
var filter = new ExpressionFilterDefinition<Sequence>(x => x.Id == sequenceName);
var builder = Builders<Sequence>.Update;
// optimistically assume value was already initialized
var update = builder.Inc(x => x.CurrentValue, 1);
var sequence = await _context.SequenceNumbers.FindOneAndUpdateAsync(
filter,
update,
new FindOneAndUpdateOptions<Sequence>
{
IsUpsert = true,
ReturnDocument = ReturnDocument.After,
});
if (sequence == null)
try
{
// we have to try to save a new sequence...
sequence = new Sequence { Id = sequenceName, CurrentValue = 1001 };
await _context.SequenceNumbers.InsertOneAsync(sequence);
}
// ...but something else could beat us to it
catch (MongoWriteException e) when (e.WriteError.Code == DuplicateKeyCode)
{
// ...so we have to retry an update
return await GetSequenceNumber(sequenceName, tryCount + 1);
}
return sequence.CurrentValue;
}
我相信还有其他选择。例如,可以使用聚合管道。