Skip to content

Latest commit

 

History

History
413 lines (200 loc) · 11.1 KB

readme.source.md

File metadata and controls

413 lines (200 loc) · 11.1 KB

Delta

Build status NuGet Status NuGet Status NuGet Status

Delta is an approach to implementing a 304 Not Modified leveraging DB change tracking.

The approach uses a last updated timestamp from the database to generate an ETag. All dynamic requests then have that ETag checked/applied.

This approach works well when the frequency of updates is relatively low. In this scenario, the majority of requests will leverage the result in a 304 Not Modified being returned and the browser loading the content its cache.

Effectively consumers will always receive the most current data, while the load on the server remains low.

See Milestones for release notes.

Powered by

JetBrains logo.

Assumptions

Frequency of updates to data is relatively low compared to reads

SQL Server

For SQL Server the transaction log is used (via dm_db_log_stats) if the current user has the VIEW SERVER STATE permission.

If VIEW SERVER STATE is not allowed then a combination of Change Tracking and/or Row Versioning is used.

Give the above certain kinds of operations will be detected:

Transaction Log Change Tracking Row Versioning Change Tracking
and Row Versioning
Insert
Update
Hard Delete
Soft Delete
Truncate

Postgres

Postgres required track_commit_timestamp to be enabled. This can be done using ALTER SYSTEM SET track_commit_timestamp to "on" and then restarting the Postgres service

304 Not Modified Flow

graph TD
    Request
    CalculateEtag[Calculate current ETag<br/>based on timestamp<br/>from web assembly and SQL]
    IfNoneMatch{Has<br/>If-None-Match<br/>header?}
    EtagMatch{Current<br/>Etag matches<br/>If-None-Match?}
    AddETag[Add current ETag<br/>to Response headers]
    304[Respond with<br/>304 Not-Modified]
    Request --> CalculateEtag
    CalculateEtag --> IfNoneMatch
    IfNoneMatch -->|Yes| EtagMatch
    IfNoneMatch -->|No| AddETag
    EtagMatch -->|No| AddETag
    EtagMatch -->|Yes| 304
Loading

ETag calculation logic

The ETag is calculated from a combination several parts

AssemblyWriteTime

The last write time of the web entry point assembly

snippet: AssemblyWriteTime

SQL timestamp

SQL Server

VIEW SERVER STATE permission

Transaction log is used via dm_db_log_stats.

select log_end_lsn
from sys.dm_db_log_stats(db_id())
No VIEW SERVER STATE permission

A combination of change_tracking_current_version (if tracking is enabled) and @@DBTS (row version timestamp)

declare @changeTracking bigint = change_tracking_current_version();
declare @timeStamp bigint = convert(bigint, @@dbts);

if (@changeTracking is null)
  select cast(@timeStamp as varchar)
else
  select cast(@timeStamp as varchar) + '-' + cast(@changeTracking as varchar)

Postgres

select pg_last_committed_xact();

Suffix

An optional string suffix that is dynamically calculated at runtime based on the current HttpContext.

snippet: Suffix

Combining the above

snippet: BuildEtag

NuGet

Delta is shipped as two nugets:

Only one of the above should be used.

Usage

SQL Server DB Schema

Example SQL schema:

snippet: Usage.Schema.verified.sql

Postgres DB Schema

Example SQL schema:

snippet: PostgresSchema

Add to WebApplicationBuilder

SQL Server

snippet: UseDeltaSqlServer

PostgreSQL

snippet: UseDeltaPostgres

Add to a Route Group

To add to a specific Route Group:

snippet: UseDeltaMapGroup

ShouldExecute

Optionally control what requests Delta is executed on.

snippet: ShouldExecute

Custom Connection discovery

By default, Delta uses HttpContext.RequestServices to discover the SqlConnection and SqlTransaction:

snippet: DiscoverConnection

To use custom connection discovery:

snippet: CustomDiscoveryConnection

To use custom connection and transaction discovery:

snippet: CustomDiscoveryConnectionAndTransaction

EF Usage

SqlServer DbContext using RowVersion

Enable row versioning in Entity Framework

snippet: SampleSqlServerDbContext

Postgres DbContext

Enable row versioning in Entity Framework

snippet: SamplePostgresDbContext

Add to WebApplicationBuilder

SQL Server

snippet: UseDeltaSQLServerEF

Postgres

snippet: UseDeltaPostgresEF

Add to a Route Group

To add to a specific Route Group:

snippet: UseDeltaMapGroupEF

ShouldExecute

Optionally control what requests Delta is executed on.

snippet: ShouldExecuteEF

UseResponseDiagnostics

Response diagnostics is an opt-in feature that includes extra log information in the response headers.

Enable by setting UseResponseDiagnostics to true at startup:

snippet: UseResponseDiagnostics

Response diagnostics headers are prefixed with Delta-.

Example Response header when the Request has not If-None-Match header.

Delta.SqlServer

A set of helper methods for working with SQL Server Change Tracking and SQL Server Row Versioning

Nuget: Delta.SqlServer

GetLastTimeStamp

For a SqlConnection:

snippet: GetLastTimeStampSqlConnection

For a DbContext:

snippet: GetLastTimeStampEF

GetDatabasesWithTracking

Get a list of all databases with change tracking enabled.

snippet: GetDatabasesWithTracking

Uses the following SQL:

snippet: GetTrackedDatabasesSql

GetTrackedTables

Get a list of all tracked tables in database.

snippet: GetTrackedTables

Uses the following SQL:

snippet: GetTrackedTablesSql

IsTrackingEnabled

Determine if change tracking is enabled for a database.

snippet: IsTrackingEnabled

Uses the following SQL:

snippet: IsTrackingEnabledSql

EnableTracking

Enable change tracking for a database.

snippet: EnableTracking

Uses the following SQL:

snippet: EnableTrackingSql

DisableTracking

Disable change tracking for a database and all tables within that database.

snippet: DisableTracking

Uses the following SQL:

For disabling tracking on a database:

snippet: DisableTrackingSqlDB

For disabling tracking on tables:

snippet: DisableTrackingSqlTable

SetTrackedTables

Enables change tracking for all tables listed, and disables change tracking for all tables not listed.

snippet: SetTrackedTables

Uses the following SQL:

For enabling tracking on a database:

snippet: EnableTrackingSql

For enabling tracking on tables:

snippet: EnableTrackingTableSql

For disabling tracking on tables:

snippet: DisableTrackingTableSql

Verifying behavior

The behavior of Delta can be verified as follows:

  • Open a page in the site
  • Open the browser developer tools
  • Change to the Network tab
  • Refresh the page.

Cached responses will show as 304 in the Status:

In the headers if-none-match will show in the request and etag will show in the response:

Ensure cache is not disabled

If disable cache is checked, the browser will not send the if-none-match header. This will effectively cause a cache miss server side, and the full server pipeline will execute.

Certificates and Chromium

Chromium, and hence the Chrome and Edge browsers, are very sensitive to certificate problems when determining if an item should be cached. Specifically, if a request is done dynamically (type: xhr) and the server is using a self-signed certificate, then the browser will not send the if-none-match header. Reference. If self-signed certificates are required during development in lower environment, then use FireFox to test the caching behavior.

Programmatic client usage

Delta is primarily designed to support web browsers as a client. All web browsers have the necessary 304 and caching functionally required.

In the scenario where web apis (that support using 304) are being consumed using .net as a client, consider using one of the below extensions to cache responses.

Icon

Estuary designed by Daan from The Noun Project.