internal/db: add Now method (#3887)

The Now() method returns the current transaction timestamp.
pull/4202/head
Johan Brandhorst-Satzkorn 3 years ago
parent 38536f4eef
commit dd2b373866

@ -70,6 +70,11 @@ type Reader interface {
// ScanRows will scan sql rows into the interface provided
ScanRows(ctx context.Context, rows *sql.Rows, result any) error
// Now returns the current transaction timestamp. Now will return the same
// timestamp whenever it is called within a transaction. In other words, calling
// Now at the start and at the end of a transaction will return the same value.
Now(ctx context.Context) (time.Time, error)
}
// Writer interface defines create, update and retryable transaction handlers
@ -533,6 +538,27 @@ func (rw *Db) SearchWhere(ctx context.Context, resources any, where string, args
return nil
}
// Now returns the current transaction timestamp. Now will return the same
// timestamp whenever it is called within a transaction. In other words, calling
// Now at the start and at the end of a transaction will return the same value.
func (rw *Db) Now(ctx context.Context) (time.Time, error) {
const op = "db.(*Db).Now"
// The Postgres docs define the different pre-defined time variables available:
// https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT.
// The value produced by this function is equivalent to current_timestamp.
rows, err := rw.Query(ctx, "select now()", nil)
if err != nil {
return time.Time{}, errors.Wrap(ctx, err, op, errors.WithMsg("failed to query current timestamp"))
}
var now time.Time
for rows.Next() {
if err := rw.ScanRows(ctx, rows, &now); err != nil {
return time.Time{}, errors.Wrap(ctx, err, op, errors.WithMsg("failed to query current timestamp"))
}
}
return now, nil
}
func isNil(i any) bool {
if i == nil {
return true

@ -3193,3 +3193,37 @@ func TestDb_oplogMsgsForItems(t *testing.T) {
// })
// }
// }
func TestDb_Now(t *testing.T) {
t.Parallel()
assert, require := assert.New(t), require.New(t)
conn, _ := TestSetup(t, "postgres")
rw := New(conn)
ctx := context.Background()
now, err := rw.Now(ctx)
require.NoError(err)
now2, err := rw.Now(ctx)
require.NoError(err)
// Calling Now() outside a transaction should result in different timestamps.
// Using time.Equal instead of assert.Equal since it's more consistently
// accurate for time.Time structs.
assert.False(now.Equal(now2))
_, err = rw.DoTx(ctx, StdRetryCnt, ExpBackoff{}, func(r Reader, _ Writer) error {
now3, err := r.Now(ctx)
require.NoError(err)
now4, err := r.Now(ctx)
require.NoError(err)
// Calling Now() inside a transaction should result in the same timestamp
assert.True(now3.Equal(now4))
// The timestamps within the transaction should differ from those created outside.
assert.False(now3.Equal(now))
assert.False(now3.Equal(now2))
return nil
})
require.NoError(err)
}

Loading…
Cancel
Save