summary refs log tree commit diff
path: root/synapse/storage/monthly_active_users.py
blob: 0741c7fa614e08f0f271bb2c138d0784d8e23d94 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
from twisted.internet import defer

from ._base import SQLBaseStore


class MonthlyActiveUsersStore(SQLBaseStore):
    def __init__(self, hs):
        super(MonthlyActiveUsersStore, self).__init__(None, hs)
        self._clock = hs.get_clock()
        self.max_mau_value = hs.config.max_mau_value

    def reap_monthly_active_users(self):
        """
        Cleans out monthly active user table to ensure that no stale
        entries exist.
        Return:
            defered, no return type
        """
        def _reap_users(txn):
            thirty_days_ago = (
                int(self._clock.time_msec()) - (1000 * 60 * 60 * 24 * 30)
            )

            # Query deletes the union of users that have either:
            #     * not visited in the last 30 days
            #     * exceeded the total max_mau_value threshold. Where there is
            #       an excess, more recent users are favoured - this is to cover
            #       the case where the limit has been step change reduced.
            #
            sql = """
            DELETE FROM monthly_active_users
            WHERE user_id
            IN (
                SELECT * FROM (
                    SELECT monthly_active_users.user_id
                    FROM monthly_active_users
                    LEFT JOIN (
                        SELECT user_id, max(last_seen) AS last_seen
                        FROM user_ips
                        GROUP BY user_id
                    ) AS uip ON uip.user_id=monthly_active_users.user_id
                    ORDER BY uip.last_seen desc LIMIT -1 OFFSET ?
                )
                UNION
                SELECT * FROM (
                    SELECT monthly_active_users.user_id
                    FROM monthly_active_users
                    LEFT JOIN (
                        SELECT user_id, max(last_seen) AS last_seen
                        FROM user_ips
                        GROUP BY user_id
                    ) AS uip  ON uip.user_id=monthly_active_users.user_id
                    WHERE uip.last_seen < ?
                )
            )
            """

            txn.execute(sql, (self.max_mau_value, thirty_days_ago,))

        return self.runInteraction("reap_monthly_active_users", _reap_users)

    def get_monthly_active_count(self):
        """
            Generates current count of monthly active users.abs
            return:
                defered resolves to int
        """
        def _count_users(txn):
            sql = "SELECT COALESCE(count(*), 0) FROM monthly_active_users"

            txn.execute(sql)
            count, = txn.fetchone()
            return count
        return self.runInteraction("count_users", _count_users)

    def insert_monthly_active_user(self, user_id):
        """
            Updates or inserts monthly active user member
            Arguments:
                user_id (str): user to add/update
        """
        return self._simple_insert(
            desc="insert_monthly_active_user",
            table="monthly_active_users",
            values={
                "user_id": user_id,
            },
        )

    @defer.inlineCallbacks
    def is_user_monthly_active(self, user_id):
        """
            Checks if a given user is part of the monthly active user group
            Arguments:
                user_id (str): user to add/update
            Return:
                bool : True if user part of group, False otherwise
        """
        user_present = yield self._simple_select_onecol(
            table="monthly_active_users",
            keyvalues={
                "user_id": user_id,
            },
            retcol="user_id",
            desc="is_user_monthly_active",
        )

        defer.returnValue(bool(user_present))