This article demonstrates how to perform time travel operations over fragments and metadata.
To set a timestamp range in TileDBArray() or TileDBGroup(), use either thetiledb_timestamp
argument at instantiation or the mutable field tiledb_timestamp. Note these options applies to read
mode only; for writing at arbitrary time points, you should use open_write() method. The separate interface is a design choice in order to avoid writing inadvertently at arbitrary timestamp other than current time point.
To get start with examples, please source the helpers from the Appendix.
Use demo_tstamped_group() to create a group with two array members. The array members have identical fragments with timestamps prior to group creation.
uri <- tempfile()
res <- demo_tstamped_group(uri)
grp <- tdb_group(uri)
grp
# R6Class: <TileDBGroupExp>
# → URI Basename: file9f2c79ac397f
# • Arrays: "testarray2" and "testarray1"
# • Groups: ""
The group and its members were created at the following timestamps:
# Timestamps (UTC) ----------
# Group @t0: 2026-01-29 12:27:38
# Member1 @t1: 2026-01-29 12:27:42
# Member2 @t2: 2026-01-29 12:27:45
Let’s open group object at previous time points and check its members:
# open group @t0
grp$tiledb_timestamp <- res$group_ts$t0
# check group timestamp range
group_timestamps(grp, tz = "UTC")
# Group Timestamps (ctx) • Mode (read) • TZ (UTC)
# • start: 1970-01-01 00:00:00
# • end : 2026-01-29 12:27:38
# no members @t0
grp$count_members()
# [1] 0
# open group @t1
grp$tiledb_timestamp <- res$group_ts$t1
# one member @t1
grp$count_members()
# [1] 1
# open group now
grp$tiledb_timestamp <- NULL
# 2 members @ current timestamp
grp$count_members()
# [1] 2
Note that grp has no metadata. Let’s add a key value at group’s t1 timestamp (the time when member 1 was added):
# no metadata
grp$get_metadata()
# TileDB GROUP: <R6 Class: TileDBGroupExp>
# Metadata: <key,value> • total 0
# timestamp @t1
t1 <- res$group_ts$t1
t1_char <- format(t1, tz = "UTC")
t1_char
# [1] "2026-01-29 12:27:42"
# set metadata val1 @t1
set_metadata(grp, list(val1 = t1_char), timestamp = t1)
# open group @t0
grp$tiledb_timestamp <- res$group_ts$t0
# no metadata @t0
grp$get_metadata()
# TileDB GROUP: <R6 Class: TileDBGroupExp>
# Metadata: <key,value> • total 0
# open now
grp$tiledb_timestamp <- NULL
# metadata that was set @t1
grp$get_metadata()
# TileDB GROUP: <R6 Class: TileDBGroupExp>
# Metadata: <key,value> • total 1
# • val1: '2026-01-29 12:27:42'
The helper function demo_tstamped_group() wrote two identical arrays with 3 fragments and metadata with the following timestamps:
# Timestamps (UTC) ----------
# Fragment @t0: 2025-08-18 16:12:50
# Fragment @t1: 2025-08-18 16:12:55
# Fragment @t2: 2025-08-18 16:13:01
Verify commit with expected timestamps was written on disk:
# uri array1
uri_arr1 <- grp$members$testarray1$uri
fraobj_1 <- tdb_fragments(uri_arr1)
fraobj_1$get_ifragment(1)
# ── FRAGMENT #1 ──────────────────────────────────────────────────────────────────────────────────────────────
# ❯ URI: file:///C:/Users/Constantine/AppData/Local/Temp/RtmpW2GPj3/file9f2c79ac397f/testarray1/__fragments/__1755533570000_1755533570000_6f2ea5440a1befeafae9f53a203226f0_22
# ❯ Type: sparse
# ❯ Non-empty domain:
# • id: [1, 1] (INT32)
# ❯ Size: 3.13 KiB
# ❯ Cell num: 1
# ❯ Timestamp range: [2025-08-18 16:12:50 UTC, 2025-08-18 16:12:50 UTC]
# ❯ Format version: 22
# ❯ Has consolidated metadata: FALSE
or alternatively:
fraobj_1$frag_uris()
# Fragment start_timestamp end_timestamp
# <char> <POSc> <POSc>
# 1: #1 2025-08-18 16:12:50 2025-08-18 16:12:50
# 2: #2 2025-08-18 16:12:55 2025-08-18 16:12:55
# 3: #3 2025-08-18 16:13:01 2025-08-18 16:13:01
# URI
# <char>
# 1: __1755533570000_1755533570000_6f2ea5440a1befeafae9f53a203226f0_22
# 2: __1755533575000_1755533575000_01d1ae9e6269bf4b457fbfb5a0359826_22
# 3: __1755533581000_1755533581000_1ae96fac8f9cc5b7e8e03bff730d81d4_22
Now, let’s time travel:
# toggle the result output
tiledb::set_return_as_preference("data.frame")
arrobj_1 <- grp$get_member("testarray1")
# first write @ "2025-08-18 16:12:50" UTC
arrobj_1$tiledb_timestamp <- as.POSIXct("2025-08-18 16:12:50", tz = "UTC")
# query upto "2025-08-18 16:12:50" UTC
arrobj_1$object[]
# id val
# 1 1 1
# metadata
arrobj_1$get_metadata()
# TileDB ARRAY: <R6 Class: TileDBArray>
# Metadata: <key,value> • total 1
# • key1: '2025-08-18 16:12:50'
# second write @ "2025-08-18 16:12:55" UTC
arrobj_1$tiledb_timestamp <- as.POSIXct("2025-08-18 16:12:55", tz = "UTC")
# query upto "2025-08-18 16:12:55" UTC
arrobj_1$object[]
# id val
# 1 1 1
# 2 2 2
arrobj_1$get_metadata()
# TileDB ARRAY: <R6 Class: TileDBArray>
# Metadata: <key,value> • total 2
# • key1: '2025-08-18 16:12:50'
# • key2: '2025-08-18 16:12:55'
# reset
arrobj_1$tiledb_timestamp <- NULL
# query upto now
arrobj_1$object[]
# id val
# 1 1 1
# 2 2 2
# 3 3 3
arrobj_1$get_metadata()
# TileDB ARRAY: <R6 Class: TileDBArray>
# Metadata: <key,value> • total 3
# • key1: '2025-08-18 16:12:50'
# • key2: '2025-08-18 16:12:55'
# • key3: '2025-08-18 16:13:01'
arrobj_1$close()
Consider we want to change a written commit at a new timestamp. The following example use
open_write() to write at t1 in order to change the value from 2 to 22; and because we’re not allow for duplicates, we see only the most recent update:
# open write @t1
arr <- open_write(arrobj_1,
timestamp = as.POSIXct("2025-08-18 16:12:55",
tz = "UTC"))
# check timestamps
array_timestamps(arr, tz = "UTC")
# Array Timestamps • Mode (write) • TZ (UTC)
# Temporal Range
# • start: none
# • end : 2025-08-18 16:12:55
# Open Range
# • start: 1970-01-01 00:00:00
# • end : 2025-08-18 16:12:55
# write data
arr[] <- data.frame(id = 2, val = 22L)
# close
arr <- tiledb::tiledb_array_close(arr)
rm(arr)
# print updated array
arrobj_1$reopen()
arrobj_1$object[]
# id val
# 1 1 1
# 2 2 22
# 3 3 3
Notice that fragments 2, 3 have identical timestamp range:
fraobj_1$reload_finfo()
fraobj_1$get_last_ifragments(3)
# ── FRAGMENT #2 ──────────────────────────────────────────────────────────────────────────────────────────────
# ❯ URI: file:///C:/Users/Constantine/AppData/Local/Temp/RtmpW2GPj3/file9f2c79ac397f/testarray1/__fragments/__1755533575000_1755533575000_01d1ae9e6269bf4b457fbfb5a0359826_22
# ❯ Type: sparse
# ❯ Non-empty domain:
# • id: [2, 2] (INT32)
# ❯ Size: 3.13 KiB
# ❯ Cell num: 1
# ❯ Timestamp range: [2025-08-18 16:12:55 UTC, 2025-08-18 16:12:55 UTC]
# ❯ Format version: 22
# ❯ Has consolidated metadata: FALSE
#
# ── FRAGMENT #3 ──────────────────────────────────────────────────────────────────────────────────────────────
# ❯ URI: file:///C:/Users/Constantine/AppData/Local/Temp/RtmpW2GPj3/file9f2c79ac397f/testarray1/__fragments/__1755533575000_1755533575000_47dcf6db63ae4d3ccafd388aadc164b7_22
# ❯ Type: sparse
# ❯ Non-empty domain:
# • id: [2, 2] (INT32)
# ❯ Size: 3.14 KiB
# ❯ Cell num: 1
# ❯ Timestamp range: [2025-08-18 16:12:55 UTC, 2025-08-18 16:12:55 UTC]
# ❯ Format version: 22
# ❯ Has consolidated metadata: FALSE
#
# ── FRAGMENT #4 ──────────────────────────────────────────────────────────────────────────────────────────────
# ❯ URI: file:///C:/Users/Constantine/AppData/Local/Temp/RtmpW2GPj3/file9f2c79ac397f/testarray1/__fragments/__1755533581000_1755533581000_1ae96fac8f9cc5b7e8e03bff730d81d4_22
# ❯ Type: sparse
# ❯ Non-empty domain:
# • id: [3, 3] (INT32)
# ❯ Size: 3.13 KiB
# ❯ Cell num: 1
# ❯ Timestamp range: [2025-08-18 16:13:01 UTC, 2025-08-18 16:13:01 UTC]
# ❯ Format version: 22
# ❯ Has consolidated metadata: FALSE
demo_tstamped_arrays <- function(uri, frags = 3) {
ts <- as.POSIXct(c("2025-08-18 16:12:50",
"2025-08-18 16:12:55",
"2025-08-18 16:13:01"),
tz = "UTC")
df <- data.frame(id = 1:frags, val = 1:frags)
tiledb::fromDataFrame(df, uri, col_index = 1, mode = "schema_only", allows_dups = FALSE)
out <- vector("numeric", frags)
arr <- tiledb::tiledb_array(uri)
for (i in seq_len(frags) ) {
tm <- ts[i]
arr <- tiledb::tiledb_array_open_at(arr, "WRITE", timestamp = tm)
arr[] <- data.frame(id = i, val = i)
arr <- tiledb::tiledb_array_open_at(arr, "WRITE", timestamp = tm)
tiledb::tiledb_put_metadata(arr, paste0("key", i), as.character(tm))
arr <- tiledb::tiledb_array_close(arr)
}
ts
}
demo_tstamped_group <- function(uri) {
ctx <- tiledb::tiledb_ctx(cached = FALSE)
group_uri <- uri
uri1 <- R6.tiledb:::file_path(group_uri, "testarray1")
uri2 <- R6.tiledb:::file_path(group_uri, "testarray2")
# create group @ t0
grp <- tiledb::tiledb_group_create(group_uri, ctx = ctx)
t0 <- Sys.time()
Sys.sleep(2)
# create arr1 and add as member @ t1
arr1 <- demo_tstamped_arrays(uri1)
grp <- tiledb::tiledb_group(group_uri, type = "WRITE", ctx = ctx)
tiledb::tiledb_group_add_member(
grp = grp,
uri = uri1,
relative = FALSE,
name = basename(uri1)
)
grp <- tiledb::tiledb_group_close(grp)
t1 <- Sys.time()
Sys.sleep(2)
# create arr2 and add as member @ t2
arr2 <- demo_tstamped_arrays(uri2)
grp <- tiledb::tiledb_group_open(grp, type = "WRITE")
tiledb::tiledb_group_add_member(
grp = grp,
uri = uri2,
relative = FALSE,
name = basename(uri2)
)
grp <- tiledb::tiledb_group_close(grp)
t2 <- Sys.time()
list(uri = group_uri,
group_ts = list(t0 = t0, t1 = t1, t2 = t2),
arr1_ts = arr1,
arr2_ts = arr2)
}