Skip to content

Commit 7fe0ff3

Browse files
authored
Add storage method to fetch all timestamps in one query (#3518)
* Add storage method to fetch all timestamps in one query * Fix missing covered line * Order by -last_modified by default
1 parent b5a59ad commit 7fe0ff3

File tree

5 files changed

+83
-0
lines changed

5 files changed

+83
-0
lines changed

kinto/core/storage/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,21 @@ def resource_timestamp(self, resource_name, parent_id):
8787
"""
8888
raise NotImplementedError
8989

90+
def all_resources_timestamps(self, resource_name):
91+
"""Get the highest timestamp of every objects in this `resource_name` for
92+
each `parent_id`.
93+
94+
.. note::
95+
96+
This should take deleted objects into account.
97+
98+
:param str resource_name: the resource name.
99+
100+
:returns: the latest timestamp of the resource by `parent_id`.
101+
:rtype: dict[str, int]
102+
"""
103+
raise NotImplementedError
104+
90105
def create(
91106
self,
92107
resource_name,

kinto/core/storage/memory.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ def resource_timestamp(self, resource_name, parent_id):
153153
raise exceptions.ReadonlyError(message=error_msg)
154154
return self.bump_and_store_timestamp(resource_name, parent_id)
155155

156+
@synchronized
157+
def all_resources_timestamps(self, resource_name):
158+
return {k: v[resource_name] for k, v in self._timestamps.items() if resource_name in v}
159+
156160
def bump_and_store_timestamp(
157161
self, resource_name, parent_id, obj=None, modified_field=None, last_modified=None
158162
):

kinto/core/storage/postgresql/__init__.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,36 @@ def resource_timestamp(self, resource_name, parent_id):
247247

248248
return obj.last_epoch
249249

250+
def all_resources_timestamps(self, resource_name):
251+
query = """
252+
WITH existing_timestamps AS (
253+
-- Timestamp of latest object by parent_id.
254+
(
255+
SELECT parent_id, MAX(last_modified) AS last_modified
256+
FROM objects
257+
WHERE resource_name = :resource_name
258+
GROUP BY parent_id
259+
)
260+
-- Timestamp of resources without sub-objects.
261+
UNION
262+
(
263+
SELECT parent_id, last_modified
264+
FROM timestamps
265+
WHERE resource_name = :resource_name
266+
)
267+
)
268+
SELECT parent_id, MAX(as_epoch(last_modified)) AS last_modified
269+
FROM existing_timestamps
270+
GROUP BY parent_id
271+
ORDER BY last_modified DESC
272+
"""
273+
with self.client.connect(readonly=True) as conn:
274+
result = conn.execute(sa.text(query), dict(resource_name=resource_name))
275+
rows = result.fetchmany(self._max_fetch_size + 1)
276+
277+
results = {r[0]: r[1] for r in rows}
278+
return results
279+
250280
@deprecate_kwargs({"collection_id": "resource_name", "record": "obj"})
251281
def create(
252282
self,

kinto/core/storage/testing.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,39 @@ def test_timestamp_are_incremented_on_delete(self):
783783
after = self.storage.resource_timestamp(**self.storage_kw)
784784
self.assertTrue(before < after)
785785

786+
def test_all_timestamps_by_parent_id(self):
787+
self.storage.create(obj={"id": "main"}, resource_name="bucket", parent_id="")
788+
self.storage.create(obj={"id": "cid1"}, resource_name="collection", parent_id="/main")
789+
self.storage.create(obj={"id": "cid2"}, resource_name="collection", parent_id="/main")
790+
self.storage.create(obj={}, resource_name="record", parent_id="/main/cid2")
791+
self.storage.create(obj={}, resource_name="record", parent_id="/main/cid2")
792+
793+
self.assertEqual(
794+
{
795+
"": self.storage.resource_timestamp(resource_name="bucket", parent_id=""),
796+
},
797+
self.storage.all_resources_timestamps(resource_name="bucket"),
798+
)
799+
self.assertEqual(
800+
{
801+
"/main": self.storage.resource_timestamp(
802+
resource_name="collection", parent_id="/main"
803+
),
804+
},
805+
self.storage.all_resources_timestamps(resource_name="collection"),
806+
)
807+
self.assertEqual(
808+
{
809+
"/main/cid1": self.storage.resource_timestamp(
810+
resource_name="record", parent_id="/main/cid1"
811+
),
812+
"/main/cid2": self.storage.resource_timestamp(
813+
resource_name="record", parent_id="/main/cid2"
814+
),
815+
},
816+
self.storage.all_resources_timestamps(resource_name="record"),
817+
)
818+
786819
@skip_if_ci
787820
def test_timestamps_are_unique(self): # pragma: no cover
788821
obtained = []

tests/core/test_storage.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def test_mandatory_overrides(self):
6666
(self.storage.initialize_schema,),
6767
(self.storage.flush,),
6868
(self.storage.resource_timestamp, "", ""),
69+
(self.storage.all_resources_timestamps, ""),
6970
(self.storage.create, "", "", {}),
7071
(self.storage.get, "", "", ""),
7172
(self.storage.update, "", "", "", {}),

0 commit comments

Comments
 (0)