1111using System ;
1212using System . Data ;
1313using System . Data . Common ;
14-
1514using NHibernate . Engine ;
1615using NHibernate . Exceptions ;
1716using NHibernate . Impl ;
@@ -26,88 +25,76 @@ public partial class DbTimestampType : TimestampType
2625 {
2726
2827 /// <inheritdoc />
29- public override Task < object > SeedAsync ( ISessionImplementor session , CancellationToken cancellationToken )
28+ public override async Task < object > SeedAsync ( ISessionImplementor session , CancellationToken cancellationToken )
3029 {
31- if ( cancellationToken . IsCancellationRequested )
30+ cancellationToken . ThrowIfCancellationRequested ( ) ;
31+ if ( session == null )
3232 {
33- return Task . FromCanceled < object > ( cancellationToken ) ;
33+ log . Debug ( "incoming session was null; using current vm time" ) ;
34+ return await ( base . SeedAsync ( null , cancellationToken ) ) . ConfigureAwait ( false ) ;
3435 }
35- try
36+ else if ( ! session . Factory . Dialect . SupportsCurrentTimestampSelection )
3637 {
37- if ( session == null )
38- {
39- log . Debug ( "incoming session was null; using current vm time" ) ;
40- return base . SeedAsync ( null , cancellationToken ) ;
41- }
42- else if ( ! session . Factory . Dialect . SupportsCurrentTimestampSelection )
43- {
44- log . Debug ( "falling back to vm-based timestamp, as dialect does not support current timestamp selection" ) ;
45- return base . SeedAsync ( session , cancellationToken ) ;
46- }
47- else
48- {
49- return GetCurrentTimestampAsync ( session , cancellationToken ) ;
50- }
38+ log . Debug ( "falling back to vm-based timestamp, as dialect does not support current timestamp selection" ) ;
39+ return await ( base . SeedAsync ( session , cancellationToken ) ) . ConfigureAwait ( false ) ;
5140 }
52- catch ( Exception ex )
41+ else
5342 {
54- return Task . FromException < object > ( ex ) ;
43+ return await ( GetCurrentTimestampAsync ( session , cancellationToken ) ) . ConfigureAwait ( false ) ;
5544 }
5645 }
5746
58- private Task < object > GetCurrentTimestampAsync ( ISessionImplementor session , CancellationToken cancellationToken )
47+ protected virtual async Task < DateTime > GetCurrentTimestampAsync ( ISessionImplementor session , CancellationToken cancellationToken )
5948 {
60- if ( cancellationToken . IsCancellationRequested )
61- {
62- return Task . FromCanceled < object > ( cancellationToken ) ;
63- }
64- try
65- {
66- Dialect . Dialect dialect = session . Factory . Dialect ;
67- string timestampSelectString = dialect . CurrentTimestampSelectString ;
68- return UsePreparedStatementAsync ( timestampSelectString , session , cancellationToken ) ;
69- }
70- catch ( Exception ex )
71- {
72- return Task . FromException < object > ( ex ) ;
73- }
49+ cancellationToken . ThrowIfCancellationRequested ( ) ;
50+ var dialect = session . Factory . Dialect ;
51+ // Need to round notably for Sql Server DateTime with Odbc, which has a 3.33ms resolution,
52+ // causing stale data update failure 2/3 of times if not rounded to 10ms.
53+ return Round (
54+ await ( UsePreparedStatementAsync ( dialect . CurrentTimestampSelectString , session , cancellationToken ) ) . ConfigureAwait ( false ) ,
55+ dialect . TimestampResolutionInTicks ) ;
7456 }
7557
76- protected virtual async Task < object > UsePreparedStatementAsync ( string timestampSelectString , ISessionImplementor session , CancellationToken cancellationToken )
58+ protected virtual async Task < DateTime > UsePreparedStatementAsync ( string timestampSelectString , ISessionImplementor session , CancellationToken cancellationToken )
7759 {
7860 cancellationToken . ThrowIfCancellationRequested ( ) ;
7961 var tsSelect = new SqlString ( timestampSelectString ) ;
8062 DbCommand ps = null ;
8163 DbDataReader rs = null ;
82- using ( new SessionIdLoggingContext ( session . SessionId ) )
83- try
84- {
85- ps = await ( session . Batcher . PrepareCommandAsync ( CommandType . Text , tsSelect , EmptyParams , cancellationToken ) ) . ConfigureAwait ( false ) ;
86- rs = await ( session . Batcher . ExecuteReaderAsync ( ps , cancellationToken ) ) . ConfigureAwait ( false ) ;
87- await ( rs . ReadAsync ( cancellationToken ) ) . ConfigureAwait ( false ) ;
88- DateTime ts = rs . GetDateTime ( 0 ) ;
89- if ( log . IsDebugEnabled )
90- {
91- log . Debug ( "current timestamp retreived from db : " + ts + " (tiks=" + ts . Ticks + ")" ) ;
92- }
93- return ts ;
94- }
95- catch ( DbException sqle )
64+ using ( new SessionIdLoggingContext ( session . SessionId ) )
9665 {
97- throw ADOExceptionHelper . Convert ( session . Factory . SQLExceptionConverter , sqle ,
98- "could not select current db timestamp" , tsSelect ) ;
99- }
100- finally
101- {
102- if ( ps != null )
66+ try
10367 {
104- try
68+ ps = await ( session . Batcher . PrepareCommandAsync ( CommandType . Text , tsSelect , EmptyParams , cancellationToken ) ) . ConfigureAwait ( false ) ;
69+ rs = await ( session . Batcher . ExecuteReaderAsync ( ps , cancellationToken ) ) . ConfigureAwait ( false ) ;
70+ await ( rs . ReadAsync ( cancellationToken ) ) . ConfigureAwait ( false ) ;
71+ var ts = rs . GetDateTime ( 0 ) ;
72+ if ( log . IsDebugEnabled )
10573 {
106- session . Batcher . CloseCommand ( ps , rs ) ;
74+ log . Debug ( "current timestamp retreived from db : " + ts + " (tiks=" + ts . Ticks + ")" ) ;
10775 }
108- catch ( DbException sqle )
76+ return ts ;
77+ }
78+ catch ( DbException sqle )
79+ {
80+ throw ADOExceptionHelper . Convert (
81+ session . Factory . SQLExceptionConverter ,
82+ sqle ,
83+ "could not select current db timestamp" ,
84+ tsSelect ) ;
85+ }
86+ finally
87+ {
88+ if ( ps != null )
10989 {
110- log . Warn ( "unable to clean up prepared statement" , sqle ) ;
90+ try
91+ {
92+ session . Batcher . CloseCommand ( ps , rs ) ;
93+ }
94+ catch ( DbException sqle )
95+ {
96+ log . Warn ( "unable to clean up prepared statement" , sqle ) ;
97+ }
11198 }
11299 }
113100 }
0 commit comments