Skip to content

Commit

Permalink
Avoid in memory load of data and update DB directly in single SQL query
Browse files Browse the repository at this point in the history
  • Loading branch information
schwartz-concordium committed Aug 15, 2023
1 parent f5089a6 commit c41d7b5
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 29 deletions.
41 changes: 26 additions & 15 deletions backend/Application/Api/GraphQL/Import/BlockWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public async Task UpdateTotalAmountLockedInReleaseSchedules(Block block)
await context.SaveChangesAsync();
}

public async Task<FinalizationTimeUpdate[]> UpdateFinalizationTimeOnBlocksInFinalizationProof(BlockInfo block, ImportState importState)
internal async Task<FinalizationTimeUpdate> UpdateFinalizationTimeOnBlocksInFinalizationProof(BlockInfo block, ImportState importState)
{
using var counter = _metrics.MeasureDuration(nameof(BlockWriter), nameof(UpdateFinalizationTimeOnBlocksInFinalizationProof));

Expand All @@ -124,27 +124,38 @@ public async Task<FinalizationTimeUpdate[]> UpdateFinalizationTimeOnBlocksInFina

if (finalizedBlockHeights.Length != 1) throw new InvalidOperationException();
var maxBlockHeight = finalizedBlockHeights.Single();
if (maxBlockHeight == importState.MaxBlockHeightWithUpdatedFinalizationTime)
var finalizationTimeUpdate = new FinalizationTimeUpdate(importState.MaxBlockHeightWithUpdatedFinalizationTime, maxBlockHeight);
if (finalizationTimeUpdate.MinBlockHeight == finalizationTimeUpdate.MaxBlockHeight)
{
return Array.Empty<FinalizationTimeUpdate>();
return finalizationTimeUpdate;
}

var minBlockHeight = importState.MaxBlockHeightWithUpdatedFinalizationTime;

var result = await context.Blocks
.Where(x => x.BlockHeight > minBlockHeight && x.BlockHeight <= maxBlockHeight)
.Select(x => new FinalizationTimeUpdate(x.BlockHeight, x.BlockSlotTime,
Math.Round((block.BlockSlotTime - x.BlockSlotTime).TotalSeconds, 1)
))
.ToArrayAsync();

const string sql = @"
update graphql_blocks
set
block_stats_finalization_time_secs = sub_query.finalizationSeconds
from (
select
block_height,
ROUND(EXTRACT(EPOCH FROM (@BlockSlotTime - block_slot_time)),1) as finalizationSeconds
from graphql_blocks
where block_height > @MinBlockHeight
and block_height <= @MaxBlockHeight
) as sub_query
where graphql_blocks.block_height = sub_query.block_height";

var conn = context.Database.GetDbConnection();
await conn.ExecuteAsync(
"update graphql_blocks set block_stats_finalization_time_secs = @FinalizationTimeSecs where block_height = @BlockHeight",
result);
sql,
new
{
finalizationTimeUpdate.MinBlockHeight,
finalizationTimeUpdate.MaxBlockHeight,
block.BlockSlotTime
});

importState.MaxBlockHeightWithUpdatedFinalizationTime = maxBlockHeight;

return result;
return finalizationTimeUpdate;
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace Application.Api.GraphQL.Import;

public record FinalizationTimeUpdate(long BlockHeight, DateTimeOffset BlockSlotTime, double FinalizationTimeSecs);
internal readonly record struct FinalizationTimeUpdate(long MinBlockHeight, long MaxBlockHeight);
23 changes: 17 additions & 6 deletions backend/Application/Api/GraphQL/Import/MetricsWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,29 @@ public async Task AddAccountsMetrics(BlockInfo blockInfo, AccountInfo[] createdA
importState.CumulativeAccountsCreated = cumulativeAccountsCreated;
}

public async Task UpdateFinalizationTimes(FinalizationTimeUpdate[] updates)
internal async Task UpdateFinalizationTimes(FinalizationTimeUpdate update)
{
if (updates.Length == 0) return;
if (update.MaxBlockHeight == update.MinBlockHeight) return;

using var counter = _metrics.MeasureDuration(nameof(MetricsWriter), nameof(UpdateFinalizationTimes));

var sql = @"update metrics_blocks
set finalization_time_secs = @FinalizationTimeSecs
where time = @BlockSlotTime and block_height = @BlockHeight";
const string sql = @"
update metrics_blocks
set
finalization_time_secs = sub_query.block_stats_finalization_time_secs
from (
select
block_height,
block_stats_finalization_time_secs
from graphql_blocks
where block_height > @MinBlockHeight
and block_height <= @MaxBlockHeight
) as sub_query
where metrics_blocks.block_height = sub_query.block_height
";
await using var conn = new NpgsqlConnection(_settings.ConnectionString);
conn.Open();
await conn.ExecuteAsync(sql, updates);
await conn.ExecuteAsync(sql, new {update.MinBlockHeight, update.MaxBlockHeight});
}

public async Task AddBakerMetrics(DateTimeOffset blockSlotTime, BakerUpdateResults results, ImportState importState)
Expand Down
1 change: 0 additions & 1 deletion backend/Application/Import/ImportChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ public BlockDataPayload(
public IList<BlockItemSummary> BlockItemSummaries { get; }
public IChainParameters ChainParameters { get; }
public IList<ISpecialEvent> SpecialEvents { get; }
public FinalizationSummary? FinalizationSummary { get; }
public AccountInfosRetrieved AccountInfos { get; }
public RewardOverviewBase RewardStatus { get; }

Expand Down
14 changes: 8 additions & 6 deletions backend/Tests/Api/GraphQL/Import/BlockWriterTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ public async Task UpdateFinalizedBlocks_FinalizationProofForSingleBlock()
const string lastBlockHash = "5c0a11302f4098572c4741905b071d958066e0550d03c3186c4483fd920155a1";

var baseTime = new DateTimeOffset(2010, 10, 05, 12, 30, 20, 123, TimeSpan.Zero);
const double secondsToAdd = 9.57;
var block = new BlockBuilder()
.WithBlockHeight(10)
.WithBlockSlotTime(baseTime)
Expand All @@ -179,15 +180,15 @@ public async Task UpdateFinalizedBlocks_FinalizationProofForSingleBlock()
await AddBlock(block);

var blockInfo = new BlockInfoBuilder()
.WithBlockSlotTime(baseTime.AddSeconds(9))
.WithBlockSlotTime(baseTime.AddSeconds(secondsToAdd))
.WithBlockLastFinalized(BlockHash.From(lastBlockHash))
.Build();

await _target.UpdateFinalizationTimeOnBlocksInFinalizationProof(blockInfo, _importState);

var dbContext = _dbContextFactory.CreateDbContext();
var result = await dbContext.Blocks.SingleAsync(x => x.BlockHeight == 10);
result.BlockStatistics.FinalizationTime.Should().Be(9);
result.BlockStatistics.FinalizationTime.Should().Be(Math.Round(secondsToAdd, 1));

_importState.MaxBlockHeightWithUpdatedFinalizationTime.Should().Be(10);
}
Expand All @@ -197,15 +198,16 @@ public async Task UpdateFinalizedBlocks_FinalizationProofForMultipleBlocks()
{
var baseTime = new DateTimeOffset(2010, 10, 05, 12, 30, 20, 123, TimeSpan.Zero);
const string lastBlockHash = "9408d0d26faf8b4cc99722ab27b094b8a27b251d8133ae690ea92b68caa689a2";

const double secondsToAdd = 40.52;

await AddBlock(new BlockBuilder().WithBlockHeight(10).WithBlockSlotTime(baseTime.AddSeconds(10)).WithBlockHash("5c0a11302f4098572c4741905b071d958066e0550d03c3186c4483fd920155a1").Build());
await AddBlock(new BlockBuilder().WithBlockHeight(11).WithBlockSlotTime(baseTime.AddSeconds(19)).WithBlockHash("01cc0746f74640292e2f1bcc5fd4a542678c88c7a840adfca365612278160845").Build());
await AddBlock(new BlockBuilder().WithBlockHeight(12).WithBlockSlotTime(baseTime.AddSeconds(31)).WithBlockHash("9408d0d26faf8b4cc99722ab27b094b8a27b251d8133ae690ea92b68caa689a2").Build());

_importState.MaxBlockHeightWithUpdatedFinalizationTime = 10;

var blockInfo = new BlockInfoBuilder()
.WithBlockSlotTime(baseTime.AddSeconds(40))
.WithBlockSlotTime(baseTime.AddSeconds(secondsToAdd))
.WithBlockLastFinalized(BlockHash.From(lastBlockHash))
.Build();

Expand All @@ -214,8 +216,8 @@ public async Task UpdateFinalizedBlocks_FinalizationProofForMultipleBlocks()
var dbContext = _dbContextFactory.CreateDbContext();
var result = await dbContext.Blocks.ToArrayAsync();
result.Should().ContainSingle(x => x.BlockHeight == 10).Which.BlockStatistics.FinalizationTime.Should().BeNull();
result.Should().ContainSingle(x => x.BlockHeight == 11).Which.BlockStatistics.FinalizationTime.Should().Be(21);
result.Should().ContainSingle(x => x.BlockHeight == 12).Which.BlockStatistics.FinalizationTime.Should().Be(9);
result.Should().ContainSingle(x => x.BlockHeight == 11).Which.BlockStatistics.FinalizationTime.Should().Be(21.5);
result.Should().ContainSingle(x => x.BlockHeight == 12).Which.BlockStatistics.FinalizationTime.Should().Be(9.5);

_importState.MaxBlockHeightWithUpdatedFinalizationTime.Should().Be(12);
}
Expand Down

0 comments on commit c41d7b5

Please sign in to comment.