private void ClearDeviceMessages(Transaction transaction)
        {
            var deviceMsgs = transaction.edus.Where(m => m.edu_type == "m.direct_to_device").ToList()
                             .ConvertAll(m => m.StreamId);

            var deviceLists = transaction.edus.Where(m => m.edu_type == "m.device_list_update").ToList()
                              .ConvertAll(m => Tuple.Create(m.StreamId, (string)m.content["user_id"]));

            using (var db = new SynapseDbContext(_connString))
            {
                if (deviceMsgs.Count != 0)
                {
                    _destLastDeviceMsgStreamId[transaction.destination] = deviceMsgs.Max();
                    var deviceMsgEntries = db.DeviceFederationOutboxes.Where(m => deviceMsgs.Contains(m.StreamId));

                    if (deviceMsgEntries.Any())
                    {
                        db.DeviceFederationOutboxes.RemoveRange(deviceMsgEntries);
                        db.SaveChanges();
                    }
                    else
                    {
                        log.Warning("No messages to delete in outbox, despite sending messages in this txn");
                    }
                }

                if (deviceLists.Count == 0)
                {
                    return;
                }

                _destLastDeviceListStreamId[transaction.destination] = deviceLists.Max(e => e.Item1);

                var deviceListEntries = db.DeviceListsOutboundPokes
                                        .Where(m =>
                                               deviceLists.FindIndex(e => e.Item1 == m.StreamId &&
                                                                     e.Item2 == m.UserId) >= 0);

                if (deviceListEntries.Any())
                {
                    foreach (var msg in deviceListEntries)
                    {
                        msg.Sent = true;
                    }

                    db.SaveChanges();
                }
                else
                {
                    log.Warning("No device lists to mark as sent, despite sending lists in this txn");
                }
            }
        }
        private void UpdateFederationPos(string type, int id)
        {
            using (var db = new SynapseDbContext(connectionString))
            {
                var res = db.FederationStreamPosition.SingleOrDefault(r => r.Type == type);

                if (res != null)
                {
                    res.StreamId = id;
                    db.SaveChanges();
                }
            }
        }
        private async Task ProcessPendingEvents()
        {
            List <EventJsonSet> events;
            var top = _lastEventPoke;
            int last;

            // Get the set of events we need to process.
            using (var db = new SynapseDbContext(_connString))
            {
                last   = (await db.FederationStreamPosition.SingleAsync(m => m.Type == "events")).StreamId;
                events = db.GetAllNewEventsStream(last, top, MAX_PDUS_PER_TRANSACTION);
            }

            if (events.Count == 0)
            {
                log.Debug("No new events to handle");
                return;
            }

            if (events.Count == MAX_PDUS_PER_TRANSACTION)
            {
                log.Warning("More than {Max} events behind", MAX_PDUS_PER_TRANSACTION);
                top = events.Last().StreamOrdering;
            }

            log.Information("Processing from {last} to {top}", last, top);

            // Skip any events that didn't come from us.
            foreach (var item in events.Where(e => IsMineId(e.Sender)).GroupBy(e => e.RoomId))
            {
                var hosts = await GetHostsInRoom(item.Key);

                foreach (var roomEvent in item)
                {
                    // TODO: Support send_on_bahalf_of?

                    IPduEvent pduEv;

                    // NOTE: This is an event format version, not room version.
                    if (roomEvent.Version == 1)
                    {
                        pduEv = new PduEventV1
                        {
                            event_id = roomEvent.EventId
                        }
                    }
                    ;
                    else // Default to latest event format version.
                    {
                        pduEv = new PduEventV2();
                    }

                    pduEv.content          = roomEvent.Content["content"] as JObject;
                    pduEv.origin           = _serverName;
                    pduEv.depth            = (long)roomEvent.Content["depth"];
                    pduEv.auth_events      = roomEvent.Content["auth_events"];
                    pduEv.prev_events      = roomEvent.Content["prev_events"];
                    pduEv.origin_server_ts = (long)roomEvent.Content["origin_server_ts"];

                    if (roomEvent.Content.ContainsKey("redacts"))
                    {
                        pduEv.redacts = (string)roomEvent.Content["redacts"];
                    }

                    pduEv.room_id    = roomEvent.RoomId;
                    pduEv.sender     = roomEvent.Sender;
                    pduEv.prev_state = roomEvent.Content["prev_state"];

                    if (roomEvent.Content.ContainsKey("state_key"))
                    {
                        pduEv.state_key = (string)roomEvent.Content["state_key"];
                    }

                    pduEv.type     = (string)roomEvent.Content["type"];
                    pduEv.unsigned = (JObject)roomEvent.Content["unsigned"];
                    pduEv.hashes   = roomEvent.Content["hashes"];

                    pduEv.signatures = new Dictionary <string, Dictionary <string, string> >();

                    foreach (var sigHosts in (JObject)roomEvent.Content["signatures"])
                    {
                        pduEv.signatures.Add(sigHosts.Key, new Dictionary <string, string>());

                        foreach (var sigs in (JObject)sigHosts.Value)
                        {
                            pduEv.signatures[sigHosts.Key].Add(sigs.Key, sigs.Value.Value <string>());
                        }
                    }

                    //TODO: I guess we need to fetch the destinations for each event in a room, because someone may have got banned in between.
                    foreach (var host in hosts)
                    {
                        var transaction = GetOrCreateTransactionForDest(host);
                        transaction.pdus.Add(pduEv);
                        AttemptTransaction(host);
                    }
                }
            }

            using (var db = new SynapseDbContext(_connString))
            {
                log.Debug("Saving position {top} to DB", top);
                var streamPos = db.FederationStreamPosition.First(e => e.Type == "events");
                streamPos.StreamId = top;
                db.SaveChanges();
            }

            // Still behind?
            if (events.Count == MAX_PDUS_PER_TRANSACTION)
            {
                log.Information("Calling ProcessPendingEvents again because we are still behind");
                await ProcessPendingEvents();
            }
        }