C# Akka.net日志读取器丢失事件

C# Akka.net日志读取器丢失事件,c#,akka.net,akka.net-persistence,akka.net-streams,C#,Akka.net,Akka.net Persistence,Akka.net Streams,在我们的应用程序中,我们使用Akka.net和事件源。持久参与者将其事件保存在SQL Server数据库中。 我们还有视图参与者,他们使用日志阅读器/持久性查询订阅这些事件,以创建具体化视图。数据库中有一个表,每个视图参与者都有一行。此行包含视图参与者的名称和上次事件成功的偏移量。 乍一看,这项工作进展顺利。然而,有时,当我们运行一个导致数千个事件的测试时,日志阅读器会丢失一些事件 视图参与者是接收者参与者。启动时,它从数据库检索最后处理的事件偏移量(从参与者的构造函数调用)。偏移量通过管道传输

在我们的应用程序中,我们使用Akka.net和事件源。持久参与者将其事件保存在SQL Server数据库中。 我们还有视图参与者,他们使用日志阅读器/持久性查询订阅这些事件,以创建具体化视图。数据库中有一个表,每个视图参与者都有一行。此行包含视图参与者的名称和上次事件成功的偏移量。 乍一看,这项工作进展顺利。然而,有时,当我们运行一个导致数千个事件的测试时,日志阅读器会丢失一些事件

视图参与者是接收者参与者。启动时,它从数据库检索最后处理的事件偏移量(从参与者的构造函数调用)。偏移量通过管道传输到OffsetMessage中的self。 收到OffsetMessage后,view actor初始化日志读取器。在接收事件(在EventEnvelope消息中)时,视图将更新

从日志读取器运行的操作首先将一行写入日志。该行包含事件偏移量。 EventEnvelope接收处理程序还向日志写入一行。该行还包含事件偏移量

我们有一个测试,结果是9635个事件被插入了日志中。有时日志读取器和EventEnvelope接收处理程序记录的事件少于9635个。 他们都记录了相同的数字,所以似乎杂志读者错过了这些事件。日志中缺失的事件与视图中缺失的项目相对应。 我们在一个空数据库上运行测试。日志记录处于调试级别,不显示异常。缺失事件(我们已经看到1到4个)可以是第一个、中间或最后一个事件。每次都不一样

到目前为止,我们还不知道是什么导致了这个问题,也不知道如何解决这个问题

下面是我们的代码片段。视图参与者都继承自一个基类:ViewActorBase

internal abstract class ViewActorBase : ReceiveActor, ILogReceive
{
    public ViewActorBase()
    {
        // Some initialisation code
        ....

        this.Receive<OffsetMessage>(this.HandleOffsetMessage);
        this.ReceiveAsync<EventEnvelope>(this.UpdateState);

        var sender = this.Sender;
        var self = this.Self;
        this.GetViewActorOffset(self, sender);
    }

    private void HandleOffsetMessage(OffsetMessage offsetMessage)
    {
        this.InitialiseJournalReader(offsetMessage.Offset);
    }

    private void InitialiseJournalReader(long offset)
    {
        // obtain read journal by plugin id
        var readJournal = PersistenceQuery.Get(Context.System).ReadJournalFor<SqlReadJournal>($"akka.persistence.query");

        // materialize stream, consuming events
        var materializer = ActorMaterializer.Create(Context.System);

        // issue query to journal
        Source<EventEnvelope, NotUsed> source = readJournal.EventsByTag(this.QueryEventTag, new Sequence(offset));

        var self = this.Self;
        source.RunForeach(envelope => { this.Logger.Debug("{Date:HH:mm:ss.fffff} JournalReader.Tell {Offset}", DateTime.Now, (envelope.Offset as Sequence).Value); self.Tell(envelope); }, materializer);
    }

    private void GetViewActorOffset(IActorRef self, IActorRef sender)
    {
        // Initialise repository
        ....

        repository.GetViewActorOffset(this.GetViewName()).PipeTo(self, sender, offset => new OffsetMessage(offset));
    }
}

internal class MyViewActor : ViewActorBase
{
    protected override async Task UpdateState(EventEnvelope envelope)
    {
        var offset = (envelope.Offset as Sequence).Value;

        this.Logger.Debug("{Date:HH:mm:ss.fffff} {MethodName} {Offset}", DateTime.Now, $"{this.GetType().Name}.UpdateState", offset);

        // Update views
        ....
    }
}
内部抽象类ViewActorBase:ReceiveActor,ILogReceive
{
公共ViewActorBase()
{
//一些初始化代码
....
this.Receive(this.HandleOffsetMessage);
this.ReceiveAsync(this.updateEstate);
var sender=this.sender;
var self=这个.self;
this.getViewActorofset(self,sender);
}
私有无效HandleOffsetMessage(OffsetMessage OffsetMessage)
{
这个.InitialiseJournalReader(offsetMessage.Offset);
}
专用void InitialiseJournalReader(长偏移量)
{
//通过插件id获取已读日志
var readJournal=PersistenceQuery.Get(Context.System.ReadJournalFor($“akka.persistence.query”);
//物化流,消耗事件
var materializer=actormatarializer.Create(Context.System);
//对日记账的问题查询
Source Source=readJournal.EventsByTag(this.QueryEventTag,新序列(偏移量));
var self=这个.self;
source.RunForeach(envelope=>{this.Logger.Debug({Date:HH:mm:ss.fffff}JournalReader.Tell{Offset}),DateTime.Now(envelope.Offset作为序列).Value);self.Tell(envelope);},物化器);
}
私有无效GetViewActorofSet(IActorRef自身,IActorRef发送方)
{
//初始化存储库
....
repository.getViewActorofset(this.GetViewName()).PipeTo(self、sender、offset=>newoffsetmessage(offset));
}
}
内部类MyViewActor:ViewActorBase
{
受保护的覆盖异步任务UpdateEstate(EventEnvelope信封)
{
var offset=(envelope.offset作为序列)。值;
Debug({Date:HH:mm:ss.fffff}{MethodName}{Offset}),DateTime.Now,$“{this.GetType().Name}.UpdateState”,Offset);
//更新视图
....
}
}
我们的代码或体系结构是否有问题?有更好的解决方案吗

其他信息 我们已经使用SQLServerProfiler运行了一些测试来监视对数据库的查询

在事件日志上执行了一个查询,请求从偏移量204743开始的100个事件。结果包含61行

<Event id="10" name="RPC:Completed">
  <Column id="1" name="TextData">exec sp_executesql N'
        SELECT TOP (@Take)
        e.PersistenceId as PersistenceId, 
        e.SequenceNr as SequenceNr, 
        e.Timestamp as Timestamp, 
        e.IsDeleted as IsDeleted, 
        e.Manifest as Manifest, 
        e.Payload as Payload,
        e.SerializerId as SerializerId,
        e.Ordering as Ordering
        FROM dbo.EventJournal e
        WHERE e.Ordering &gt; @Ordering AND e.Tags LIKE @Tag
        ORDER BY Ordering ASC
        ',N'@Tag nvarchar(10),@Ordering bigint,@Take bigint',@Tag=N'%;Module;%',@Ordering=204743,@Take=100</Column>
  <Column id="9" name="ClientProcessID">1169425116</Column>
  <Column id="10" name="ApplicationName">Core .Net SqlClient Data Provider</Column>
  <Column id="12" name="SPID">82</Column>
  <Column id="13" name="Duration">353890</Column>
  <Column id="14" name="StartTime">2018-08-30T16:32:32.927+02:00</Column>
  <Column id="15" name="EndTime">2018-08-30T16:32:33.28+02:00</Column>
  <Column id="16" name="Reads">326</Column>
  <Column id="17" name="Writes">0</Column>
  <Column id="18" name="CPU">0</Column>
  <Column id="48" name="RowCounts">61</Column>
</Event>

exec sp_executesql N'
选择顶部(@Take)
e、 PersistenceId作为PersistenceId,
e、 SequenceNr作为SequenceNr,
e、 时间戳作为时间戳,
e、 被删除为被删除,
e、 以舱单形式显示,
e、 有效载荷作为有效载荷,
e、 SerializerId作为SerializerId,
e、 按顺序排序
来自dbo.EventJournal e
其中e.Ordering@Ordering和e.标记,如@Tag
按订单订购ASC
“,N'@Tag nvarchar(10),@Ordering bigint,@Take bigint',@Tag=N%”;模块;%'@订购量=204743,@Take=100
1169425116
核心.Net SqlClient数据提供程序
82
353890
2018-08-30T16:32:32.927+02:00
2018-08-30T16:32:33.28+02:00
326
0
0
61
我们将下一个查询扩展到204804(204743+61)。然而,它是从204810年开始的。为什么它跳过(或错过)6项活动