예제 #1
0
        public async Task Run(ReplayOptions opts)
        {
            _logger.Information("Starting replay (type: {Type}, after: {After})", opts.Type, opts.After);

            using var hasher = new SibrHasher();
            var updates = _updateStore.ExportAllUpdatesRaw(UpdateType.Stream, new UpdateStore.EntityVersionQuery {
                After = opts.After
            });

            var sw = new Stopwatch();

            await using var conn = await _db.Obtain();

            await foreach (var chunk in updates.Buffer(200))
            {
                var extracted = chunk.SelectMany(streamUpdate =>
                {
                    var obj = JObject.Parse(streamUpdate.Data.GetRawText());
                    return(TgbUtils.ExtractUpdatesFromStreamRoot(streamUpdate.SourceId, streamUpdate.Timestamp, obj, hasher, opts.Type).EntityUpdates);
                }).ToList();

                sw.Restart();
                await using var tx = await conn.BeginTransactionAsync();

                var saved = await _updateStore.SaveUpdates(conn, extracted, false);

                await tx.CommitAsync();

                sw.Stop();

                var timestamp = chunk.Min(u => u.Timestamp);
                _logger.Information("@ {Timestamp}: Saved {NewUpdateCount}/{UpdateCount} updates from {StreamObjects} stream objects (took {Duration})",
                                    timestamp, saved, extracted.Count, chunk.Count, sw.Elapsed);
            }
        }
예제 #2
0
        // Make this an AsyncEnumerable so we can still get partial data saved if the servers break in the middle of a pull
        private async IAsyncEnumerable <EntityUpdate> FetchElectionResults(int season)
        {
            using var hasher = new SibrHasher();

            var recap = await _client.GetJsonAsync($"https://www.blaseball.com/database/offseasonRecap?season={season}");

            yield return(EntityUpdate.From(UpdateType.OffseasonRecap, _sourceId, recap.Timestamp, recap.Data, hasher));

            var decreeIds     = recap.Data.Value <JArray>("decreeResults").Values <string>().ToList();
            var decreeResults = await GetUpdatesByIds(UpdateType.DecreeResult, "https://www.blaseball.com/database/decreeResults", decreeIds, hasher);

            _logger.Information("Fetched {DecreeCount} decree results", decreeIds.Count);
            foreach (var result in decreeResults)
            {
                yield return(result);
            }

            var bonusIds     = recap.Data.Value <JArray>("bonusResults").Values <string>().ToList();
            var bonusResults = await GetUpdatesByIds(UpdateType.BonusResult, "https://www.blaseball.com/database/bonusResults", bonusIds, hasher);

            _logger.Information("Fetched {BonusCount} bonus results", bonusIds.Count);
            foreach (var result in bonusResults)
            {
                yield return(result);
            }

            var eventIds     = recap.Data.Value <JArray>("eventResults").Values <string>().ToList();
            var eventResults = await GetUpdatesByIds(UpdateType.EventResult, "https://www.blaseball.com/database/eventResults", eventIds, hasher);

            _logger.Information("Fetched {EventCount} event results", eventIds.Count);
            foreach (var result in eventResults)
            {
                yield return(result);
            }
        }
예제 #3
0
        private async Task HandleStreamData(string obj)
        {
            var timestamp = _clock.GetCurrentInstant();
            var data      = JObject.Parse(obj);

            await using var conn = await _db.Obtain();

            await using (var tx = await conn.BeginTransactionAsync())
            {
                await _updateStore.SaveUpdate(conn, EntityUpdate.From(UpdateType.Stream, _sourceId, timestamp, data));

                await tx.CommitAsync();
            }

            await using (var tx = await conn.BeginTransactionAsync())
            {
                using var hasher = new SibrHasher();
                var extracted = TgbUtils.ExtractUpdatesFromStreamRoot(_sourceId, timestamp, data, hasher);
                var gameRes   = await _gameStore.SaveGameUpdates(conn, extracted.GameUpdates);

                var miscRes = await _updateStore.SaveUpdates(conn, extracted.EntityUpdates);

                var maxPlayCount = extracted.GameUpdates.Count > 0 ? extracted.GameUpdates.Max(gu => gu.PlayCount) : -1;

                _logger.Information(
                    "Received stream update, saved {GameUpdates} game updates, {MiscUpdates} updates, max PC {MaxPlayCount}",
                    gameRes, miscRes, maxPlayCount);

                await tx.CommitAsync();
            }
        }
예제 #4
0
        protected override async Task ProcessFile(string filename, IAsyncEnumerable <JToken> entries)
        {
            using var hasher = new SibrHasher();
            var streamUpdates = new List <EntityUpdate>();
            var miscUpdates   = new List <EntityUpdate>();
            var gameUpdates   = new List <GameUpdate>();
            var gameIds       = new HashSet <Guid>();

            var count = 0;

            await using var conn = await _db.Obtain();

            await foreach (var entry in entries)
            {
                var timestamp = ExtractTimestamp(entry);
                if (timestamp == null)
                {
                    continue;
                }

                var root = FindStreamRoot(entry as JObject);
                streamUpdates.Add(EntityUpdate.From(UpdateType.Stream, _sourceId, timestamp.Value, root));

                var updates = TgbUtils.ExtractUpdatesFromStreamRoot(_sourceId, timestamp.Value, root, hasher);
                gameUpdates.AddRange(updates.GameUpdates);
                miscUpdates.AddRange(updates.EntityUpdates);

                gameIds.UnionWith(updates.GameUpdates.Select(g => g.GameId));
                if (count++ % 100 == 0)
                {
                    await FlushUpdates(conn, streamUpdates, gameUpdates, miscUpdates);
                }
            }

            await FlushUpdates(conn, streamUpdates, gameUpdates, miscUpdates);

            await _gameStore.TryAddNewGameIds(conn, gameUpdates.Select(gu => gu.GameId));

            await _gameUpdateStore.UpdateSearchIndex(conn);
        }
예제 #5
0
        public async Task Run(ReplayOptions opts)
        {
            _logger.Information("Starting replay (type: {Type}, start: {Start}, end: {End})",
                                opts.Type != null ? string.Join(",", opts.Type) : null, opts.Start, opts.End);

            using var hasher = new SibrHasher();

            var sw = new Stopwatch();

            await using var conn = await _db.Obtain();

            var page = opts.Start != null ? new PageToken(opts.Start.Value, default) : null;

            while (true)
            {
                var chunk = await _updateStore.ExportAllUpdatesChunked(conn, UpdateType.Stream,
                                                                       new UpdateStore.EntityVersionQuery
                {
                    Page   = page,
                    Before = opts.End,
                    Order  = SortOrder.Asc,
                    Count  = 500
                });

                if (chunk.Count == 0)
                {
                    break;
                }
                page = chunk.Last().NextPage;

                // if (opts.Type == UpdateType.Game)
                if (false) // todo: uhhh how do we do this
                {
                    var extractedGameUpdates = chunk.SelectMany(streamUpdate =>
                    {
                        var obj = JObject.Parse(streamUpdate.Data.GetRawText());
                        return(TgbUtils.ExtractUpdatesFromStreamRoot(streamUpdate.SourceId, streamUpdate.Timestamp, obj, hasher, opts.Type).GameUpdates);
                    }).ToList();

                    sw.Restart();
                    await using var tx = await conn.BeginTransactionAsync();

                    var savedGameUpdates = await _gameUpdateStore.SaveGameUpdates(conn, extractedGameUpdates, false);

                    await tx.CommitAsync();

                    sw.Stop();

                    var timestamp = chunk.Min(u => u.Timestamp);
                    _logger.Information("@ {Timestamp}: Saved {GameUpdateCount}/{UpdateCount} game updates from {StreamObjects} stream objects (took {Duration})",
                                        timestamp, savedGameUpdates, extractedGameUpdates.Count, chunk.Count, sw.Elapsed);
                }
                else
                {
                    var extractedUpdates = chunk.SelectMany(streamUpdate =>
                    {
                        var obj = JObject.Parse(streamUpdate.Data.GetRawText());
                        return(TgbUtils.ExtractUpdatesFromStreamRoot(streamUpdate.SourceId, streamUpdate.Timestamp, obj,
                                                                     hasher, opts.Type).EntityUpdates);
                    }).ToList();

                    sw.Restart();
                    await using var tx = await conn.BeginTransactionAsync();

                    var savedUpdates = await _updateStore.SaveUpdates(conn, extractedUpdates, false, append : false);

                    await tx.CommitAsync();

                    sw.Stop();

                    var timestamp = chunk.Min(u => u.Timestamp);
                    _logger.Information("@ {Timestamp}: Saved {NewUpdateCount}/{UpdateCount} updates from {StreamObjects} stream objects (took {Duration})",
                                        timestamp, savedUpdates, extractedUpdates.Count, chunk.Count, sw.Elapsed);
                }
            }
        }
예제 #6
0
        private async Task <IEnumerable <EntityUpdate> > GetUpdatesByIds(UpdateType type, string baseUrl, IEnumerable <string> ids, SibrHasher hasher)
        {
            var(timestamp, data) = await _client.GetJsonAsync($"{baseUrl}?ids={string.Join(',', ids)}");

            return(data.Values <JObject>().Select(obj =>
            {
                var id = TgbUtils.GenerateGuidFromString(obj.Value <string>("id"));
                return EntityUpdate.From(type, _sourceId, timestamp, obj, hasher, id);
            }));
        }
예제 #7
0
 public static GameUpdate From(Guid sourceId, Instant timestamp, JToken data, SibrHasher hasher = null) =>
예제 #8
0
 public static EntityUpdate From(UpdateType type, Guid sourceId, Instant timestamp, JToken data, SibrHasher hasher = null, Guid?idOverride = null) =>