Skip to content

Commit de2ae41

Browse files
authored
Support creation and expiration time in DatabaseImpl (#4836)
* Support creation and expiration time in DatabaseImpl * address comments
1 parent 4b0153a commit de2ae41

File tree

3 files changed

+163
-13
lines changed

3 files changed

+163
-13
lines changed

java/arcs/android/storage/database/DatabaseImpl.kt

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ class DatabaseImpl(
184184
storage_keys.value_id,
185185
storage_keys.data_type,
186186
entities.entity_id,
187+
entities.creation_timestamp,
188+
entities.expiration_timestamp,
187189
entities.version_map,
188190
entities.version_number
189191
FROM storage_keys
@@ -199,10 +201,12 @@ class DatabaseImpl(
199201
val storageKeyId = it.getLong(0)
200202
val schemaTypeId = it.getLong(1)
201203
val entityId = it.getString(3)
202-
val versionMap = requireNotNull(it.getVersionMap(4)) {
204+
val creationTimestamp = it.getLong(4)
205+
val expirationTimestamp = it.getLong(5)
206+
val versionMap = requireNotNull(it.getVersionMap(6)) {
203207
"No VersionMap available for Entity at $storageKey"
204208
}
205-
val versionNumber = it.getInt(5)
209+
val versionNumber = it.getInt(7)
206210
// Fetch the entity's fields.
207211
counters?.increment(DatabaseCounters.GET_ENTITY_FIELDS)
208212
val fieldsByName = getSchemaFields(schemaTypeId, db)
@@ -239,7 +243,9 @@ class DatabaseImpl(
239243
RawEntity(
240244
id = entityId,
241245
singletons = singletons,
242-
collections = collections
246+
collections = collections,
247+
creationTimestamp = creationTimestamp,
248+
expirationTimestamp = expirationTimestamp
243249
),
244250
schema,
245251
versionNumber,
@@ -275,7 +281,12 @@ class DatabaseImpl(
275281
db: SQLiteDatabase
276282
): Any = db.rawQuery(
277283
"""
278-
SELECT entity_id, backing_storage_key, version_map
284+
SELECT
285+
entity_id,
286+
backing_storage_key,
287+
version_map,
288+
creation_timestamp,
289+
expiration_timestamp
279290
FROM entity_refs
280291
WHERE id = ?
281292
""".trimIndent(),
@@ -284,7 +295,9 @@ class DatabaseImpl(
284295
Reference(
285296
id = it.getString(0),
286297
storageKey = StorageKeyParser.parse(it.getString(1)),
287-
version = it.getVersionMap(2)
298+
version = it.getVersionMap(2),
299+
creationTimestamp = it.getLong(3),
300+
expirationTimestamp = it.getLong(4)
288301
)
289302
} ?: throw IllegalArgumentException("Entity Reference with ID $entityRefId does not exist.")
290303

@@ -377,6 +390,8 @@ class DatabaseImpl(
377390
val storageKeyId = createEntityStorageKeyId(
378391
storageKey,
379392
entity.id,
393+
entity.creationTimestamp,
394+
entity.expirationTimestamp,
380395
schemaTypeId,
381396
data.versionMap,
382397
data.databaseVersion,
@@ -749,6 +764,8 @@ class DatabaseImpl(
749764
suspend fun createEntityStorageKeyId(
750765
storageKey: StorageKey,
751766
entityId: String,
767+
creationTimestamp: Long,
768+
expirationTimestamp: Long,
752769
typeId: TypeId,
753770
versionMap: VersionMap,
754771
databaseVersion: Int,
@@ -759,7 +776,10 @@ class DatabaseImpl(
759776
counters?.increment(DatabaseCounters.GET_ENTITY_STORAGEKEY_ID)
760777
rawQuery(
761778
"""
762-
SELECT storage_keys.data_type, entities.entity_id, entities.version_number
779+
SELECT
780+
storage_keys.data_type,
781+
entities.entity_id,
782+
entities.version_number
763783
FROM storage_keys
764784
LEFT JOIN entities ON storage_keys.id = entities.storage_key_id
765785
WHERE storage_keys.storage_key = ?
@@ -806,6 +826,8 @@ class DatabaseImpl(
806826
ContentValues().apply {
807827
put("storage_key_id", storageKeyId)
808828
put("entity_id", entityId)
829+
put("creation_timestamp", creationTimestamp)
830+
put("expiration_timestamp", expirationTimestamp)
809831
put("version_map", versionMap.toProtoLiteral())
810832
put("version_number", databaseVersion)
811833
}
@@ -852,6 +874,8 @@ class DatabaseImpl(
852874
null,
853875
ContentValues().apply {
854876
put("entity_id", reference.id)
877+
put("creation_timestamp", reference.creationTimestamp)
878+
put("expiration_timestamp", reference.expirationTimestamp)
855879
put("backing_storage_key", reference.storageKey.toString())
856880
reference.version?.let {
857881
put("version_map", it.toProtoLiteral())
@@ -950,7 +974,12 @@ class DatabaseImpl(
950974
db: SQLiteDatabase
951975
): Set<Reference> = db.rawQuery(
952976
"""
953-
SELECT entity_refs.entity_id, entity_refs.backing_storage_key, entity_refs.version_map
977+
SELECT
978+
entity_refs.entity_id,
979+
entity_refs.creation_timestamp,
980+
entity_refs.expiration_timestamp,
981+
entity_refs.backing_storage_key,
982+
entity_refs.version_map
954983
FROM collection_entries
955984
JOIN entity_refs ON collection_entries.value_id = entity_refs.id
956985
WHERE collection_entries.collection_id = ?
@@ -959,8 +988,10 @@ class DatabaseImpl(
959988
).map {
960989
Reference(
961990
id = it.getString(0),
962-
storageKey = StorageKeyParser.parse(it.getString(1)),
963-
version = it.getVersionMap(2)
991+
storageKey = StorageKeyParser.parse(it.getString(3)),
992+
version = it.getVersionMap(4),
993+
creationTimestamp = it.getString(1).toLong(),
994+
expirationTimestamp = it.getString(2).toLong()
964995
)
965996
}.toSet()
966997

@@ -1227,6 +1258,8 @@ class DatabaseImpl(
12271258
CREATE TABLE entities (
12281259
storage_key_id INTEGER NOT NULL PRIMARY KEY,
12291260
entity_id TEXT NOT NULL,
1261+
creation_timestamp INTEGER NOT NULL,
1262+
expiration_timestamp INTEGER NOT NULL,
12301263
-- Serialized VersionMapProto for the entity.
12311264
version_map TEXT NOT NULL,
12321265
-- Monotonically increasing version number for the entity.
@@ -1239,6 +1272,8 @@ class DatabaseImpl(
12391272
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
12401273
-- The ID for the entity (Arcs string ID, not a row ID).
12411274
entity_id TEXT NOT NULL,
1275+
creation_timestamp INTEGER NOT NULL,
1276+
expiration_timestamp INTEGER NOT NULL,
12421277
-- The storage key for the backing store for this entity.
12431278
backing_storage_key TEXT NOT NULL,
12441279
-- Serialized VersionMapProto for the reference, if available.

java/arcs/core/storage/Reference.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,28 @@ data class Reference(
3535

3636
override suspend fun dereference(coroutineContext: CoroutineContext): RawEntity? =
3737
requireNotNull(dereferencer).dereference(this, coroutineContext)
38+
39+
/** Entity creation time (in millis). */
40+
@Suppress("GoodTime") // use Instant
41+
override var creationTimestamp: Long = RawEntity.UNINITIALIZED_TIMESTAMP
42+
set(value) {
43+
require(this.creationTimestamp == RawEntity.UNINITIALIZED_TIMESTAMP) {
44+
"cannot override creationTimestamp $value"
45+
}
46+
@Suppress("GoodTime") // use Instant
47+
field = value
48+
}
49+
50+
/** Entity expiration time (in millis). */
51+
@Suppress("GoodTime") // use Instant
52+
override var expirationTimestamp: Long = RawEntity.UNINITIALIZED_TIMESTAMP
53+
set(value) {
54+
require(this.expirationTimestamp == RawEntity.UNINITIALIZED_TIMESTAMP) {
55+
"cannot override expirationTimestamp $value"
56+
}
57+
@Suppress("GoodTime") // use Instant
58+
field = value
59+
}
3860
}
3961

4062
/** Defines an object capable of de-referencing a [Reference]. */
@@ -48,3 +70,18 @@ interface Dereferencer<T> {
4870
/** Converts any [Referencable] object into a reference-mode-friendly [Reference] object. */
4971
fun Referencable.toReference(storageKey: StorageKey, version: VersionMap) =
5072
Reference(id, storageKey, version)
73+
74+
fun Reference(
75+
id: ReferenceId,
76+
storageKey: StorageKey,
77+
version: VersionMap?,
78+
creationTimestamp: Long,
79+
expirationTimestamp: Long
80+
) = Reference(
81+
id,
82+
storageKey,
83+
version
84+
).also {
85+
it.creationTimestamp = creationTimestamp
86+
it.expirationTimestamp = expirationTimestamp
87+
}

javatests/arcs/android/storage/database/DatabaseImplTest.kt

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ class DatabaseImplTest {
179179
database.createEntityStorageKeyId(
180180
DummyStorageKey("key1"),
181181
"eid1",
182+
CREATION_TIMESTAMP,
183+
EXPIRATION_TIMESTAMP,
182184
123L,
183185
VERSION_MAP,
184186
FIRST_VERSION_NUMBER,
@@ -190,6 +192,8 @@ class DatabaseImplTest {
190192
database.createEntityStorageKeyId(
191193
DummyStorageKey("key2"),
192194
"eid2",
195+
CREATION_TIMESTAMP,
196+
EXPIRATION_TIMESTAMP,
193197
123L,
194198
VERSION_MAP,
195199
FIRST_VERSION_NUMBER,
@@ -201,6 +205,8 @@ class DatabaseImplTest {
201205
database.createEntityStorageKeyId(
202206
DummyStorageKey("key3"),
203207
"eid3",
208+
CREATION_TIMESTAMP,
209+
EXPIRATION_TIMESTAMP,
204210
123L,
205211
VERSION_MAP,
206212
FIRST_VERSION_NUMBER,
@@ -217,6 +223,8 @@ class DatabaseImplTest {
217223
database.createEntityStorageKeyId(
218224
DummyStorageKey("key1"),
219225
"eid1",
226+
CREATION_TIMESTAMP,
227+
EXPIRATION_TIMESTAMP,
220228
123L,
221229
VERSION_MAP,
222230
1,
@@ -228,6 +236,8 @@ class DatabaseImplTest {
228236
database.createEntityStorageKeyId(
229237
DummyStorageKey("key2"),
230238
"eid2",
239+
CREATION_TIMESTAMP,
240+
EXPIRATION_TIMESTAMP,
231241
123L,
232242
VERSION_MAP,
233243
1,
@@ -241,6 +251,8 @@ class DatabaseImplTest {
241251
database.createEntityStorageKeyId(
242252
DummyStorageKey("key1"),
243253
"eid1",
254+
CREATION_TIMESTAMP,
255+
EXPIRATION_TIMESTAMP,
244256
123L,
245257
VERSION_MAP,
246258
2,
@@ -252,6 +264,8 @@ class DatabaseImplTest {
252264
database.createEntityStorageKeyId(
253265
DummyStorageKey("key2"),
254266
"eid2",
267+
CREATION_TIMESTAMP,
268+
EXPIRATION_TIMESTAMP,
255269
123L,
256270
VERSION_MAP,
257271
2,
@@ -266,6 +280,8 @@ class DatabaseImplTest {
266280
database.createEntityStorageKeyId(
267281
key,
268282
"correct-entity-id",
283+
CREATION_TIMESTAMP,
284+
EXPIRATION_TIMESTAMP,
269285
123L,
270286
VERSION_MAP,
271287
FIRST_VERSION_NUMBER,
@@ -276,6 +292,8 @@ class DatabaseImplTest {
276292
database.createEntityStorageKeyId(
277293
key,
278294
"incorrect-entity-id",
295+
CREATION_TIMESTAMP,
296+
EXPIRATION_TIMESTAMP,
279297
123L,
280298
VERSION_MAP,
281299
FIRST_VERSION_NUMBER,
@@ -293,11 +311,29 @@ class DatabaseImplTest {
293311
val key = DummyStorageKey("key")
294312
val entityId = "entity-id"
295313
val typeId = 123L
296-
database.createEntityStorageKeyId(key, entityId, typeId, VERSION_MAP, 10, db)
314+
database.createEntityStorageKeyId(
315+
key,
316+
entityId,
317+
CREATION_TIMESTAMP,
318+
EXPIRATION_TIMESTAMP,
319+
typeId,
320+
VERSION_MAP,
321+
10,
322+
db
323+
)
297324

298325
// Same version number is rejected.
299326
val exception1 = assertSuspendingThrows(IllegalArgumentException::class) {
300-
database.createEntityStorageKeyId(key, entityId, typeId, VERSION_MAP, 10, db)
327+
database.createEntityStorageKeyId(
328+
key,
329+
entityId,
330+
CREATION_TIMESTAMP,
331+
EXPIRATION_TIMESTAMP,
332+
typeId,
333+
VERSION_MAP,
334+
10,
335+
db
336+
)
301337
}
302338
assertThat(exception1).hasMessageThat().isEqualTo(
303339
"Given version (10) must be greater than version in database (10) when updating " +
@@ -306,15 +342,33 @@ class DatabaseImplTest {
306342

307343
// Smaller version number is rejected.
308344
val exception2 = assertSuspendingThrows(IllegalArgumentException::class) {
309-
database.createEntityStorageKeyId(key, entityId, typeId, VERSION_MAP, 9, db)
345+
database.createEntityStorageKeyId(
346+
key,
347+
entityId,
348+
CREATION_TIMESTAMP,
349+
EXPIRATION_TIMESTAMP,
350+
typeId,
351+
VERSION_MAP,
352+
9,
353+
db
354+
)
310355
}
311356
assertThat(exception2).hasMessageThat().isEqualTo(
312357
"Given version (9) must be greater than version in database (10) when updating " +
313358
"storage key dummy://key."
314359
)
315360

316361
// Increasing version number is ok.
317-
database.createEntityStorageKeyId(key, entityId, typeId, VERSION_MAP, 11, db)
362+
database.createEntityStorageKeyId(
363+
key,
364+
entityId,
365+
CREATION_TIMESTAMP,
366+
EXPIRATION_TIMESTAMP,
367+
typeId,
368+
VERSION_MAP,
369+
11,
370+
db
371+
)
318372
}
319373

320374
@Test
@@ -480,6 +534,26 @@ class DatabaseImplTest {
480534
assertThat(entityOut).isEqualTo(entity)
481535
}
482536

537+
@Test
538+
fun insertAndGet_entity_withCreationAndExpiration() = runBlockingTest {
539+
val key = DummyStorageKey("key")
540+
val schema = newSchema(
541+
"hash",
542+
SchemaFields(singletons = mapOf("text" to FieldType.Text), collections = mapOf())
543+
)
544+
val entity = DatabaseData.Entity(
545+
RawEntity("entity", mapOf("text" to "abc".toReferencable()), mapOf(), 11L, 111L),
546+
schema,
547+
FIRST_VERSION_NUMBER,
548+
VERSION_MAP
549+
)
550+
551+
database.insertOrUpdate(key, entity)
552+
database.dumpTables("storage_keys", "entities", "fields", "field_values")
553+
val entityOut = database.getEntity(key, schema)
554+
assertThat(entityOut).isEqualTo(entity)
555+
}
556+
483557
@Test
484558
fun insertAndGet_entity_newEntityWithReferenceFields() = runBlockingTest {
485559
val key = DummyStorageKey("key")
@@ -1260,6 +1334,10 @@ class DatabaseImplTest {
12601334
private val TEXT_TYPE_ID = PrimitiveType.Text.ordinal.toLong()
12611335
private val BOOLEAN_TYPE_ID = PrimitiveType.Boolean.ordinal.toLong()
12621336
private val NUMBER_TYPE_ID = PrimitiveType.Number.ordinal.toLong()
1337+
1338+
private val CREATION_TIMESTAMP = 99L
1339+
private val EXPIRATION_TIMESTAMP = 999L
1340+
12631341
}
12641342
}
12651343

0 commit comments

Comments
 (0)