Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Add an admin API for server media statistics #8723

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/8723.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add an admin API for local server media statistics. Contributed by @dklimpel.
47 changes: 47 additions & 0 deletions docs/admin_api/statistics.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,50 @@
# Server media usage statistics

Returns information about all local media usage of this server.
Gives the possibility to filter them by time.

The API is:

```
GET /_synapse/admin/v1/statistics/server/media
```

To use it, you will need to authenticate by providing an `access_token`
for a server admin: see [README.rst](README.rst).

A response body like the following is returned:

```json
{
"media_count": 3,
"media_length": 210
}
```

To paginate, check for `next_token` and if present, call the endpoint
again with `from` set to the value of `next_token`. This will return a new page.

If the endpoint does not return a `next_token` then there are no more
reports to paginate through.

**Parameters**

The following parameters should be set in the URL:

* `from_ts` - string representing a positive integer - Considers only
files created at this timestamp or later. Unix timestamp in ms.
* `until_ts` - string representing a positive integer - Considers only
files created at this timestamp or earlier. Unix timestamp in ms.


**Response**

The following fields are returned in the JSON response body:

* `media_count` - integer - Number of uploaded media.
* `media_length` - integer - Size of uploaded media in bytes.


# Users' media usage statistics

Returns information about all local media usage of users. Gives the
Expand Down
6 changes: 5 additions & 1 deletion synapse/rest/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@
ShutdownRoomRestServlet,
)
from synapse.rest.admin.server_notice_servlet import SendServerNoticeServlet
from synapse.rest.admin.statistics import UserMediaStatisticsRestServlet
from synapse.rest.admin.statistics import (
ServerMediaStatisticsRestServlet,
UserMediaStatisticsRestServlet,
)
from synapse.rest.admin.users import (
AccountValidityRenewServlet,
DeactivateAccountRestServlet,
Expand Down Expand Up @@ -229,6 +232,7 @@ def register_servlets(hs, http_server):
DevicesRestServlet(hs).register(http_server)
DeleteDevicesRestServlet(hs).register(http_server)
UserMediaStatisticsRestServlet(hs).register(http_server)
ServerMediaStatisticsRestServlet(hs).register(http_server)
EventReportDetailRestServlet(hs).register(http_server)
EventReportsRestServlet(hs).register(http_server)
PushersRestServlet(hs).register(http_server)
Expand Down
42 changes: 42 additions & 0 deletions synapse/rest/admin/statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,45 @@ async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
ret["next_token"] = start + len(users_media)

return 200, ret


class ServerMediaStatisticsRestServlet(RestServlet):
"""
Get statistics about uploaded media on this server.
"""

PATTERNS = admin_patterns("/statistics/server/media$")

def __init__(self, hs: "HomeServer"):
self.hs = hs
self.auth = hs.get_auth()
self.store = hs.get_datastore()

async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]:
await assert_requester_is_admin(self.auth, request)

from_ts = parse_integer(request, "from_ts", default=0)
if from_ts < 0:
raise SynapseError(
400,
"Query parameter from_ts must be a string representing a positive integer.",
errcode=Codes.INVALID_PARAM,
)

until_ts = parse_integer(request, "until_ts")
if until_ts is not None:
if until_ts < 0:
raise SynapseError(
400,
"Query parameter until_ts must be a string representing a positive integer.",
errcode=Codes.INVALID_PARAM,
)
if until_ts <= from_ts:
raise SynapseError(
400,
"Query parameter until_ts must be greater than from_ts.",
errcode=Codes.INVALID_PARAM,
)

count, length = await self.store.get_server_media_usage(from_ts, until_ts)
return 200, {"media_count": count, "media_length": length}
45 changes: 45 additions & 0 deletions synapse/storage/databases/main/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -1009,3 +1009,48 @@ def get_users_media_usage_paginate_txn(txn):
return await self.db_pool.runInteraction(
"get_users_media_usage_paginate_txn", get_users_media_usage_paginate_txn
)

async def get_server_media_usage(
self, from_ts: Optional[int] = None, until_ts: Optional[int] = None,
) -> Tuple[int, int]:
"""Function to retrieve size and number of uploaded local media.

Args:
from_ts: request only media that are created later than this timestamp (ms)
until_ts: request only media that are created earlier than this timestamp (ms)
Returns:
A tuple of integer representing the total number of
media and size that exist given this query
"""

def get_server_media_usage_txn(txn):
filters = []
args = []

if from_ts:
filters.append("created_ts >= ?")
args.extend([from_ts])
if until_ts:
filters.append("created_ts <= ?")
args.extend([until_ts])

where_clause = "WHERE " + " AND ".join(filters) if len(filters) > 0 else ""

sql = """
SELECT
COUNT(*) as media_count,
COALESCE(SUM(media_length), 0) as media_length
FROM local_media_repository
{where_clause}
""".format(
where_clause=where_clause,
)

txn.execute(sql, args)
row = txn.fetchone()

return row[0], row[1]

return await self.db_pool.runInteraction(
"get_server_media_usage_txn", get_server_media_usage_txn
)
Loading