Skip to content

Commit

Permalink
Handle exceptions in JsonSizeAnalyzer
Browse files Browse the repository at this point in the history
For some reason, the analyzer may sometimes throw NullReferenceException.
Rather than failing the entire page, we now just log the error and continue.
  • Loading branch information
exyi committed Nov 11, 2023
1 parent 1e179e2 commit 2978932
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 26 deletions.
6 changes: 5 additions & 1 deletion src/Framework/Framework/Diagnostics/JsonSizeAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,12 @@ AtomicSizeProfile analyzeToken(JToken token)
return new(((bool)token) ? 4 : 5);
case JTokenType.Null:
return new(4);
case JTokenType.Guid:
return new(36 + 2);
case JTokenType.Date:
return new(23 + 2);
default:
Debug.Assert(false);
Debug.Assert(false, $"Unexpected token type {token.Type}");
return new(token.ToString().Length);
}
}
Expand Down
59 changes: 34 additions & 25 deletions src/Framework/Framework/Diagnostics/PerformanceWarningTracer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ namespace DotVVM.Framework.Diagnostics
public class PerformanceWarningTracer : IRequestTracer
{
private readonly RuntimeWarningCollector logger;
private readonly ILogger? tracerLogger;
private readonly DotvvmPerfWarningsConfiguration config;
private readonly JsonSizeAnalyzer jsonSizeAnalyzer;
private readonly Stopwatch stopwatch = Stopwatch.StartNew();
private readonly List<(string eventName, TimeSpan timestamp)> events = new();
public PerformanceWarningTracer(DotvvmConfiguration config, RuntimeWarningCollector logger, JsonSizeAnalyzer jsonSizeAnalyzer)
public PerformanceWarningTracer(DotvvmConfiguration config, RuntimeWarningCollector logger, JsonSizeAnalyzer jsonSizeAnalyzer, ILogger<PerformanceWarningTracer>? tracerLogger = null)
{
this.logger = logger;
this.tracerLogger = tracerLogger;
this.config = config.Diagnostics.PerfWarnings;
this.jsonSizeAnalyzer = jsonSizeAnalyzer;
}
Expand Down Expand Up @@ -57,34 +59,41 @@ void WarnLargeViewModel(long viewModelSize, IDotvvmRequestContext context)
if (context.ViewModelJson is null)
return;

var vmAnalysis = jsonSizeAnalyzer.Analyze(context.ViewModelJson);
try
{
var vmAnalysis = jsonSizeAnalyzer.Analyze(context.ViewModelJson);

var topClasses =
vmAnalysis.Classes
.OrderByDescending(c => c.Value.Size.ExclusiveSize)
.Take(3)
// only classes which have at least 5% impact
.Where(c => c.Value.Size.ExclusiveSize > vmAnalysis.TotalSize / 20)
.ToArray();
var topProperties =
vmAnalysis.Classes
.SelectMany(c => c.Value.Properties.Select(p => (Key: c.Key + "." + p.Key, p.Value)))
.OrderByDescending(c => c.Value.ExclusiveSize)
.Take(3)
// only properties which have at least 5% impact
.Where(c => c.Value.ExclusiveSize > vmAnalysis.TotalSize / 20)
.ToArray();
var topClasses =
vmAnalysis.Classes
.OrderByDescending(c => c.Value.Size.ExclusiveSize)
.Take(3)
// only classes which have at least 5% impact
.Where(c => c.Value.Size.ExclusiveSize > vmAnalysis.TotalSize / 20)
.ToArray();
var topProperties =
vmAnalysis.Classes
.SelectMany(c => c.Value.Properties.Select(p => (Key: c.Key + "." + p.Key, p.Value)))
.OrderByDescending(c => c.Value.ExclusiveSize)
.Take(3)
// only properties which have at least 5% impact
.Where(c => c.Value.ExclusiveSize > vmAnalysis.TotalSize / 20)
.ToArray();

var byteToPercent = 100.0 / vmAnalysis.TotalSize;
var byteToPercent = 100.0 / vmAnalysis.TotalSize;

var msg = $"The serialized view model has {viewModelSize / 1024.0 / 1024.0:0.0}MB, which may make your application quite slow. " +
string.Join(", ",
topProperties.Select(c => $"Property {c.Key} takes {c.Value.ExclusiveSize * byteToPercent:0}%").Concat(
topClasses.Select(c => $"Class {c.Key} takes {c.Value.Size.ExclusiveSize * byteToPercent:0}%")));
var msg = $"The serialized view model has {viewModelSize / 1024.0 / 1024.0:0.0}MB, which may make your application quite slow. " +
string.Join(", ",
topProperties.Select(c => $"Property {c.Key} takes {c.Value.ExclusiveSize * byteToPercent:0}%").Concat(
topClasses.Select(c => $"Class {c.Key} takes {c.Value.Size.ExclusiveSize * byteToPercent:0}%")));

logger.Warn(new DotvvmRuntimeWarning(
msg
));
logger.Warn(new DotvvmRuntimeWarning(
msg
));
}
catch (Exception ex)
{
tracerLogger?.LogWarning(ex, $"Failed to analyze view model size. The serialized view model has {viewModelSize / 1024.0 / 1024.0:0.0}MB, which may make your application quite slow");
}
}
public Task EndRequest(IDotvvmRequestContext context)
{
Expand Down

0 comments on commit 2978932

Please sign in to comment.