This document describes how to use the commit timestamp feature to track the date and time when changes are made to your database records. When you have a large database with many transactions that change records and you want to capture changes made to the datasets, the commit timestamp feature simplifies this task.
Through a series of code samples, this document shows you how to rewrite existing data manipulation language (DML) to create a change log. The document extends Using Cloud Spanner commit timestamps to create a change log with Go by demonstrating the methods using Java and JDBC. The logic is represented in SQL rather than as Spanner client objects.
How the tables work
Assume you have a facts table called msgs
and a history table called
msg_history
. Each time the facts table gets modified, you want to concurrently
save a record in the msg_history
table. You can use the contents of the
history table later for other uses—for example, for auditing or as an event
source.
The msgs
table holds the facts, represented by a transaction ID (id
) and
the message (msg
). The data definition language (DDL) statement that you use
to create the table looks like the following:
CREATE TABLE msgs (
id INT64,
msg STRING(MAX),
) PRIMARY KEY (id)
The msg_history
table holds the transaction history. The following DDL
creates the history table. The column ts
stores the commit timestamp.
CREATE TABLE msgs_history (
id INT64,
ts TIMESTAMP OPTIONS (allow_commit_timestamp=true),
previous_msg STRING(MAX)
) PRIMARY KEY (ts, id)
The examples
Instead of writing only to the facts table, you must do the operation to the history table in the same transaction. Spanner's JDBC driver supports declaration of the start and end of a transaction, which is a standard JDBC operation.
Step 1: Rewrite the insert operations
The first step is to rewrite the insert
operations from the following form:
insert into msgs (id, msg) values (1, 'a real msg')
Change the inserts to look like the following:
insert into msgs_history (id, previous_msg, ts) values (1, Null, PENDING_COMMIT_TIMESTAMP()); insert into msgs (id, msg) values (1, 'a real msg');
Here's the Java code:
// insert into history stmt.executeUpdate( "insert into msgs_history (id, previous_msg, ts)" + " values (1, Null, PENDING_COMMIT_TIMESTAMP())"); // insert into real table stmt.executeUpdate("insert into msgs (id, msg)" + " values (1, 'a real msg')"); conn.commit();
Step 2: Rewrite the update operations
Next, you rewrite the update
operations from the following form:
update msgs set msg = 'new message' where id = 1
Change the updates to look like the following:
insert msgs_history (id, previous_msg, ts) values ((select id from msgs where id =1 ), (select msg from msgs where id = 1), PENDING_COMMIT_TIMESTAMP()); update msgs set msg = 'new message' where id = 1;
Here's the Java code:
// insert into historystmt.executeUpdate( "insert msgs_history (id, previous_msg, ts)" + " values ((select id from msgs where id =1)," + " (select msg from msgs where id = 1), PENDING_COMMIT_TIMESTAMP())"); // update into fact table stmt.executeUpdate("update msgs set msg = 'new message' where id = 1"); conn.commit();
Step 3: Rewrite the delete operations
Finally, you rewrite the delete
operations from the following form:
delete from msgs where id = 1
Change the deletes to look like the following:
insert msgs_history (id, previous_msg, ts) values ((select id from msgs where id =1 ), (select msg from msgs where id = 1), PENDING_COMMIT_TIMESTAMP()); delete from msgs where id = 1;
Here's the Java code:
// insert into history stmt.executeUpdate( "insert msgs_history (id, previous_msg, ts)" + " values ((select id from msgs where id =1)," + " (select msg from msgs where id = 1), PENDING_COMMIT_TIMESTAMP()) "); // delete from real table stmt.executeUpdate("delete from msgs where id = 1"); conn.commit();
Using the history table
Access the change history as follows:
select id,msg,current_timestamp() as ts from msgs where id = 1 union all select id,previous_msg, ts as msg from msgs_history where id = 1 order by ts desc
The results look similar to the following:
id msg ts 1 new message 2020-02-07T07:44:10.24833726Z 1 a real msg 2020-01-14T10:07:20.137935Z 1 2020-01-14T10:07:20.070374Z
What's next
- Read more about the Spanner commit timestamp feature.
- Learn more about Spanner change streams.
- Explore reference architectures, diagrams, and best practices about Google Cloud. Take a look at our Cloud Architecture Center.