Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
4c79588
Update from main
Jan 27, 2025
afd9fe8
Added new test
Jan 27, 2025
a70d725
Merge branch 'main' into Add-delete-data-test
SimonCropp Jan 28, 2025
a644163
Merge branch 'main' into Add-delete-data-test
SimonCropp Feb 4, 2025
152130e
.
SimonCropp Feb 4, 2025
b44e44c
.
SimonCropp Feb 5, 2025
a35075b
.
SimonCropp Feb 5, 2025
9cc7804
.
SimonCropp Feb 5, 2025
9570d23
.
SimonCropp Feb 5, 2025
9fe3463
Update Usage.cs
SimonCropp Feb 5, 2025
914ac20
Docs changes
actions-user Feb 5, 2025
fe91b7e
.
SimonCropp Feb 5, 2025
8f12864
Merge branch 'fn_dblog' of https://github.com/SimonCropp/Delta into f…
SimonCropp Feb 5, 2025
0190578
Update DeltaExtensions_Sql.cs
SimonCropp Feb 5, 2025
39102bf
Update DeltaExtensions_Sql.cs
SimonCropp Feb 5, 2025
f7ae9ee
Docs changes
actions-user Feb 5, 2025
ab601ee
Update DeltaExtensions_Sql.cs
SimonCropp Feb 5, 2025
5aba66f
Merge branch 'fn_dblog' of https://github.com/SimonCropp/Delta into f…
SimonCropp Feb 5, 2025
b128fdb
Docs changes
actions-user Feb 5, 2025
0b923b2
Update readme.source.md
SimonCropp Feb 5, 2025
be808ec
Docs changes
actions-user Feb 5, 2025
e9d364e
Update readme.source.md
SimonCropp Feb 5, 2025
48b9d4f
Merge branch 'fn_dblog' of https://github.com/SimonCropp/Delta into f…
SimonCropp Feb 5, 2025
7c345fa
Docs changes
actions-user Feb 5, 2025
da285c0
.
SimonCropp Feb 5, 2025
08d88dc
Merge branch 'fn_dblog' of https://github.com/SimonCropp/Delta into f…
SimonCropp Feb 5, 2025
a68eea0
Update Usage.cs
SimonCropp Feb 5, 2025
64baefa
Docs changes
actions-user Feb 5, 2025
1cfc6b3
.
SimonCropp Feb 5, 2025
d36eae8
.
SimonCropp Feb 5, 2025
a846508
Revert "Update Usage.cs"
SimonCropp Feb 5, 2025
2338cf5
Update LocalDbTestBase.cs
SimonCropp Feb 5, 2025
25e7b1c
Docs changes
actions-user Feb 5, 2025
ee78c32
.
SimonCropp Feb 5, 2025
e5c908b
.
SimonCropp Feb 5, 2025
51f373e
Docs changes
actions-user Feb 5, 2025
1a848e4
Update DeltaExtensions_Sql.cs
SimonCropp Feb 5, 2025
7d3d53c
.
SimonCropp Feb 5, 2025
1406ae0
.
SimonCropp Feb 5, 2025
2eaed3d
.
SimonCropp Feb 6, 2025
c05b383
Update Usage.cs
SimonCropp Feb 6, 2025
264c2b6
.
SimonCropp Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 17 additions & 32 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ Effectively consumers will always receive the most current data, while the load
## Assumptions

* Frequency of updates to data is relatively low compared to reads
* Using SQL Server or Postgres timestamp features
* SQL Server: Using either [SQL Server Change Tracking](https://learn.microsoft.com/en-us/sql/relational-databases/track-changes/track-data-changes-sql-server) and/or [SQL Server Row Versioning](https://learn.microsoft.com/en-us/sql/t-sql/data-types/rowversion-transact-sql)
* Postgres: [track_commit_timestamp](https://www.postgresql.org/docs/17/runtime-config-replication.html#GUC-TRACK-COMMIT-TIMESTAMP) is enabled. This can be done using `ALTER SYSTEM SET track_commit_timestamp to "on"` and then restarting the Postgres service

* Using SQL Server or Postgres. Postgres required [track_commit_timestamp](https://www.postgresql.org/docs/17/runtime-config-replication.html#GUC-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
Expand Down Expand Up @@ -72,21 +69,12 @@ AssemblyWriteTime = File.GetLastWriteTime(webAssemblyLocation).Ticks.ToString();

#### SQL timestamp

A combination of [change_tracking_current_version](https://learn.microsoft.com/en-us/sql/relational-databases/system-functions/change-tracking-current-version-transact-sql) (if tracking is enabled) and [@@DBTS (row version timestamp)](https://learn.microsoft.com/en-us/sql/t-sql/functions/dbts-transact-sql)


<!-- snippet: SqlServerTimestamp -->
<a id='snippet-SqlServerTimestamp'></a>
```cs
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)
select log_end_lsn from sys.dm_db_log_stats(db_id())
```
<sup><a href='/src/Delta/DeltaExtensions_Sql.cs#L46-L54' title='Snippet source file'>snippet source</a> | <a href='#snippet-SqlServerTimestamp' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Delta/DeltaExtensions_Sql.cs#L46-L48' title='Snippet source file'>snippet source</a> | <a href='#snippet-SqlServerTimestamp' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


Expand Down Expand Up @@ -138,8 +126,6 @@ Only one of the above should be used.

### SQL Server DB Schema

Ensure [SQL Server Change Tracking](https://learn.microsoft.com/en-us/sql/relational-databases/track-changes/track-data-changes-sql-server) and/or [SQL Server Row Versioning](https://learn.microsoft.com/en-us/sql/t-sql/data-types/rowversion-transact-sql) is enabled for all relevant tables.

Example SQL schema:

<!-- snippet: Usage.Schema.verified.sql -->
Expand Down Expand Up @@ -331,25 +317,25 @@ var application = webApplicationBuilder.Build();
application.UseDelta(
getConnection: httpContext => httpContext.RequestServices.GetRequiredService<SqlConnection>());
```
<sup><a href='/src/DeltaTests/Usage.cs#L220-L226' title='Snippet source file'>snippet source</a> | <a href='#snippet-CustomDiscoveryConnection' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L303-L309' title='Snippet source file'>snippet source</a> | <a href='#snippet-CustomDiscoveryConnection' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

To use custom connection and transaction discovery:

<!-- snippet: CustomDiscoveryConnectionAndTransaction -->
<a id='snippet-CustomDiscoveryConnectionAndTransaction'></a>
```cs
var webApplication = webApplicationBuilder.Build();
webApplication.UseDelta(
var application = webApplicationBuilder.Build();
application.UseDelta(
getConnection: httpContext =>
{
var provider = httpContext.RequestServices;
var sqlConnection = provider.GetRequiredService<SqlConnection>();
var sqlTransaction = provider.GetService<SqlTransaction>();
return new(sqlConnection, sqlTransaction);
var connection = provider.GetRequiredService<SqlConnection>();
var transaction = provider.GetService<SqlTransaction>();
return new(connection, transaction);
});
```
<sup><a href='/src/DeltaTests/Usage.cs#L231-L243' title='Snippet source file'>snippet source</a> | <a href='#snippet-CustomDiscoveryConnectionAndTransaction' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L314-L326' title='Snippet source file'>snippet source</a> | <a href='#snippet-CustomDiscoveryConnectionAndTransaction' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


Expand Down Expand Up @@ -536,7 +522,7 @@ Nuget: [Delta.SqlServer](https://www.nuget.org/packages/Delta.SqlServer)
```cs
var timeStamp = await sqlConnection.GetLastTimeStamp();
```
<sup><a href='/src/DeltaTests/Usage.cs#L59-L63' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampSqlConnection' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L165-L169' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetLastTimeStampSqlConnection' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->


Expand Down Expand Up @@ -564,7 +550,7 @@ foreach (var db in trackedDatabases)
Trace.WriteLine(db);
}
```
<sup><a href='/src/DeltaTests/Usage.cs#L118-L126' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetDatabasesWithTracking' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L201-L209' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetDatabasesWithTracking' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand Down Expand Up @@ -594,7 +580,7 @@ foreach (var db in trackedTables)
Trace.WriteLine(db);
}
```
<sup><a href='/src/DeltaTests/Usage.cs#L144-L152' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetTrackedTables' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L227-L235' title='Snippet source file'>snippet source</a> | <a href='#snippet-GetTrackedTables' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand All @@ -619,7 +605,7 @@ Determine if change tracking is enabled for a database.
```cs
var isTrackingEnabled = await sqlConnection.IsTrackingEnabled();
```
<sup><a href='/src/DeltaTests/Usage.cs#L209-L213' title='Snippet source file'>snippet source</a> | <a href='#snippet-IsTrackingEnabled' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L292-L296' title='Snippet source file'>snippet source</a> | <a href='#snippet-IsTrackingEnabled' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand All @@ -646,7 +632,7 @@ Enable change tracking for a database.
```cs
await sqlConnection.EnableTracking();
```
<sup><a href='/src/DeltaTests/Usage.cs#L203-L207' title='Snippet source file'>snippet source</a> | <a href='#snippet-EnableTracking' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L286-L290' title='Snippet source file'>snippet source</a> | <a href='#snippet-EnableTracking' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand Down Expand Up @@ -674,7 +660,7 @@ Disable change tracking for a database and all tables within that database.
```cs
await sqlConnection.DisableTracking();
```
<sup><a href='/src/DeltaTests/Usage.cs#L188-L192' title='Snippet source file'>snippet source</a> | <a href='#snippet-DisableTracking' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L271-L275' title='Snippet source file'>snippet source</a> | <a href='#snippet-DisableTracking' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand Down Expand Up @@ -711,7 +697,7 @@ Enables change tracking for all tables listed, and disables change tracking for
```cs
await sqlConnection.SetTrackedTables(["Companies"]);
```
<sup><a href='/src/DeltaTests/Usage.cs#L138-L142' title='Snippet source file'>snippet source</a> | <a href='#snippet-SetTrackedTables' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/DeltaTests/Usage.cs#L221-L225' title='Snippet source file'>snippet source</a> | <a href='#snippet-SetTrackedTables' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Uses the following SQL:
Expand Down Expand Up @@ -785,7 +771,6 @@ If disable cache is checked, the browser will not send the `if-none-match` heade
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]( https://issues.chromium.org/issues/40666473). 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.
Expand Down
11 changes: 1 addition & 10 deletions readme.source.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ Effectively consumers will always receive the most current data, while the load
## Assumptions

* Frequency of updates to data is relatively low compared to reads
* Using SQL Server or Postgres timestamp features
* SQL Server: Using either [SQL Server Change Tracking](https://learn.microsoft.com/en-us/sql/relational-databases/track-changes/track-data-changes-sql-server) and/or [SQL Server Row Versioning](https://learn.microsoft.com/en-us/sql/t-sql/data-types/rowversion-transact-sql)
* Postgres: [track_commit_timestamp](https://www.postgresql.org/docs/17/runtime-config-replication.html#GUC-TRACK-COMMIT-TIMESTAMP) is enabled. This can be done using `ALTER SYSTEM SET track_commit_timestamp to "on"` and then restarting the Postgres service

* Using SQL Server or Postgres. Postgres required [track_commit_timestamp](https://www.postgresql.org/docs/17/runtime-config-replication.html#GUC-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
Expand Down Expand Up @@ -58,9 +55,6 @@ snippet: AssemblyWriteTime

#### SQL timestamp

A combination of [change_tracking_current_version](https://learn.microsoft.com/en-us/sql/relational-databases/system-functions/change-tracking-current-version-transact-sql) (if tracking is enabled) and [@@DBTS (row version timestamp)](https://learn.microsoft.com/en-us/sql/t-sql/functions/dbts-transact-sql)


snippet: SqlServerTimestamp


Expand Down Expand Up @@ -91,8 +85,6 @@ Only one of the above should be used.

### SQL Server DB Schema

Ensure [SQL Server Change Tracking](https://learn.microsoft.com/en-us/sql/relational-databases/track-changes/track-data-changes-sql-server) and/or [SQL Server Row Versioning](https://learn.microsoft.com/en-us/sql/t-sql/data-types/rowversion-transact-sql) is enabled for all relevant tables.

Example SQL schema:

snippet: Usage.Schema.verified.sql
Expand Down Expand Up @@ -343,7 +335,6 @@ If disable cache is checked, the browser will not send the `if-none-match` heade
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]( https://issues.chromium.org/issues/40666473). 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.
Expand Down
7 changes: 2 additions & 5 deletions src/Delta.EFTests/LocalDbTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@ static LocalDbTestBase() =>
},
storage: Storage.FromSuffix<SampleDbContext>("ef"));

public Task<SqlDatabase<SampleDbContext>> LocalDb(string? testSuffix = null)
=> sqlInstance.Build(testFile, null, GetName(testSuffix));

public Task<SampleDbContext> LocalDbContext(string? testSuffix = null)
=> sqlInstance.BuildContext(testFile, null, GetName(testSuffix));
public Task<SqlDatabase<SampleDbContext>> LocalDb(string? testSuffix = null) =>
sqlInstance.Build(testFile, null, GetName(testSuffix));

static string GetName(string? testSuffix)
{
Expand Down
40 changes: 7 additions & 33 deletions src/Delta.EFTests/Usage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,41 +43,16 @@ public async Task GetLastTimeStamp()
}

[Test]
public async Task LastTimeStampRowVersion()
public async Task LastTimeStamp()
{
await using var database = await LocalDb();

var context = database.Context;
var emptyTimeStamp = await context.GetLastTimeStamp();
IsNotEmpty(emptyTimeStamp);
IsNotNull(emptyTimeStamp);

var entity = new Company
{
Content = "The company"
};
await database.AddData(entity);
var addTimeStamp = await context.GetLastTimeStamp();
IsNotEmpty(addTimeStamp);
IsNotNull(addTimeStamp);
AreNotEqual(addTimeStamp, emptyTimeStamp);

entity.Content = "The company2";
await context.SaveChangesAsync();
var updateTimeStamp = await context.GetLastTimeStamp();
IsNotEmpty(updateTimeStamp);
IsNotNull(updateTimeStamp);
AreNotEqual(updateTimeStamp, addTimeStamp);
AreNotEqual(updateTimeStamp, emptyTimeStamp);
}

[Test]
public async Task LastTimeStampRowVersionAndTracking()
{
await using var database = await LocalDb();

await database.Connection.EnableTracking();
var context = database.Context;
//seed with an entity so there is something in transaction log
await database.AddData(
new Company
{
Content = "The company"
});
var emptyTimeStamp = await context.GetLastTimeStamp();
IsNotEmpty(emptyTimeStamp);
IsNotNull(emptyTimeStamp);
Expand All @@ -99,6 +74,5 @@ public async Task LastTimeStampRowVersionAndTracking()
IsNotNull(updateTimeStamp);
AreNotEqual(updateTimeStamp, addTimeStamp);
AreNotEqual(updateTimeStamp, emptyTimeStamp);

}
}
20 changes: 11 additions & 9 deletions src/Delta/DeltaExtensions_Sql.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,20 @@ static async Task<string> ExecuteTimestampQuery(DbCommand command, Cancel cancel
var name = command.GetType().Name;
if (name == "SqlCommand")
{
command.CommandText = @"
command.CommandText = $@"
-- begin-snippet: SqlServerTimestamp
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)
select log_end_lsn from sys.dm_db_log_stats(db_id())
-- end-snippet
";
return (string) (await command.ExecuteScalarAsync(cancel))!;
await using var reader = await command.ExecuteReaderAsync(CommandBehavior.SingleRow, cancel);
var readAsync = await reader.ReadAsync(cancel);
// for empty transaction log
if(!readAsync)
{
return string.Empty;
}

return (string)reader[0];
}

if (name == "NpgsqlCommand")
Expand Down
16 changes: 13 additions & 3 deletions src/DeltaTests/LocalDbTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@ public abstract class LocalDbTestBase
{
static SqlInstance sqlInstance = new(
name: "DeltaTests",
buildTemplate: DbBuilder.Create);
buildTemplate: async connection =>
{
await DbBuilder.Create(connection);
await using var command = connection.CreateCommand();
command.CommandText =
$"""
insert into [Companies] (Id, Content)
values ('{Guid.NewGuid()}', 'initial data')
""";
await command.ExecuteNonQueryAsync();
});

public Task<SqlDatabase> LocalDb(string? testSuffix = null)
=> sqlInstance.Build(testFile, null, GetName(testSuffix));
public Task<SqlDatabase> LocalDb(string? testSuffix = null) =>
sqlInstance.Build(testFile, null, GetName(testSuffix));

static string GetName(string? testSuffix)
{
Expand Down
1 change: 1 addition & 0 deletions src/DeltaTests/ModuleInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public static void Init()
DeltaExtensions.UseResponseDiagnostics = true;

#endregion

VerifierSettings.InitializePlugins();
}
}
Loading