Merge pull request #163 from matrix-org/markjh/presence_list_cache

Add a cache for the presence list
erikj/theseus
Mark Haines 2015-05-22 17:02:23 +01:00
commit d9f60e8dc8
5 changed files with 65 additions and 34 deletions

View File

@ -521,20 +521,26 @@ class PresenceHandler(BaseHandler):
if not self.hs.is_mine(observer_user): if not self.hs.is_mine(observer_user):
raise SynapseError(400, "User is not hosted on this Home Server") raise SynapseError(400, "User is not hosted on this Home Server")
presence = yield self.store.get_presence_list( presence_list = yield self.store.get_presence_list(
observer_user.localpart, accepted=accepted observer_user.localpart, accepted=accepted
) )
for p in presence: results = []
observed_user = UserID.from_string(p.pop("observed_user_id")) for row in presence_list:
p["observed_user"] = observed_user observed_user = UserID.from_string(row["observed_user_id"])
p.update(self._get_or_offline_usercache(observed_user).get_state()) result = {
if "last_active" in p: "observed_user": observed_user, "accepted": row["accepted"]
p["last_active_ago"] = int( }
self.clock.time_msec() - p.pop("last_active") result.update(
self._get_or_offline_usercache(observed_user).get_state()
)
if "last_active" in result:
result["last_active_ago"] = int(
self.clock.time_msec() - result.pop("last_active")
) )
results.append(result)
defer.returnValue(presence) defer.returnValue(results)
@defer.inlineCallbacks @defer.inlineCallbacks
@log_function @log_function

View File

@ -13,7 +13,9 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from ._base import SQLBaseStore from ._base import SQLBaseStore, cached
from twisted.internet import defer
class PresenceStore(SQLBaseStore): class PresenceStore(SQLBaseStore):
@ -87,31 +89,48 @@ class PresenceStore(SQLBaseStore):
desc="add_presence_list_pending", desc="add_presence_list_pending",
) )
@defer.inlineCallbacks
def set_presence_list_accepted(self, observer_localpart, observed_userid): def set_presence_list_accepted(self, observer_localpart, observed_userid):
return self._simple_update_one( result = yield self._simple_update_one(
table="presence_list", table="presence_list",
keyvalues={"user_id": observer_localpart, keyvalues={"user_id": observer_localpart,
"observed_user_id": observed_userid}, "observed_user_id": observed_userid},
updatevalues={"accepted": True}, updatevalues={"accepted": True},
desc="set_presence_list_accepted", desc="set_presence_list_accepted",
) )
self.get_presence_list_accepted.invalidate(observer_localpart)
defer.returnValue(result)
def get_presence_list(self, observer_localpart, accepted=None): def get_presence_list(self, observer_localpart, accepted=None):
keyvalues = {"user_id": observer_localpart} if accepted:
if accepted is not None: return self.get_presence_list_accepted(observer_localpart)
keyvalues["accepted"] = accepted else:
keyvalues = {"user_id": observer_localpart}
if accepted is not None:
keyvalues["accepted"] = accepted
return self._simple_select_list(
table="presence_list",
keyvalues=keyvalues,
retcols=["observed_user_id", "accepted"],
desc="get_presence_list",
)
@cached()
def get_presence_list_accepted(self, observer_localpart):
return self._simple_select_list( return self._simple_select_list(
table="presence_list", table="presence_list",
keyvalues=keyvalues, keyvalues={"user_id": observer_localpart, "accepted": True},
retcols=["observed_user_id", "accepted"], retcols=["observed_user_id", "accepted"],
desc="get_presence_list", desc="get_presence_list_accepted",
) )
@defer.inlineCallbacks
def del_presence_list(self, observer_localpart, observed_userid): def del_presence_list(self, observer_localpart, observed_userid):
return self._simple_delete_one( yield self._simple_delete_one(
table="presence_list", table="presence_list",
keyvalues={"user_id": observer_localpart, keyvalues={"user_id": observer_localpart,
"observed_user_id": observed_userid}, "observed_user_id": observed_userid},
desc="del_presence_list", desc="del_presence_list",
) )
self.get_presence_list_accepted.invalidate(observer_localpart)

View File

@ -233,7 +233,7 @@ class MockedDatastorePresenceTestCase(PresenceTestCase):
if not user_localpart in self.PRESENCE_LIST: if not user_localpart in self.PRESENCE_LIST:
return defer.succeed([]) return defer.succeed([])
return defer.succeed([ return defer.succeed([
{"observed_user_id": u} for u in {"observed_user_id": u, "accepted": accepted} for u in
self.PRESENCE_LIST[user_localpart]]) self.PRESENCE_LIST[user_localpart]])
datastore.get_presence_list = get_presence_list datastore.get_presence_list = get_presence_list
@ -735,9 +735,11 @@ class PresencePushTestCase(MockedDatastorePresenceTestCase):
self.assertEquals( self.assertEquals(
[ [
{"observed_user": self.u_banana, {"observed_user": self.u_banana,
"presence": OFFLINE}, "presence": OFFLINE,
"accepted": True},
{"observed_user": self.u_clementine, {"observed_user": self.u_clementine,
"presence": OFFLINE}, "presence": OFFLINE,
"accepted": True},
], ],
presence presence
) )
@ -758,9 +760,11 @@ class PresencePushTestCase(MockedDatastorePresenceTestCase):
self.assertEquals([ self.assertEquals([
{"observed_user": self.u_banana, {"observed_user": self.u_banana,
"presence": ONLINE, "presence": ONLINE,
"last_active_ago": 2000}, "last_active_ago": 2000,
"accepted": True},
{"observed_user": self.u_clementine, {"observed_user": self.u_clementine,
"presence": OFFLINE}, "presence": OFFLINE,
"accepted": True},
], presence) ], presence)
(events, _) = yield self.event_source.get_new_events_for_user( (events, _) = yield self.event_source.get_new_events_for_user(

View File

@ -101,8 +101,8 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
self.datastore.get_profile_avatar_url = get_profile_avatar_url self.datastore.get_profile_avatar_url = get_profile_avatar_url
self.presence_list = [ self.presence_list = [
{"observed_user_id": "@banana:test"}, {"observed_user_id": "@banana:test", "accepted": True},
{"observed_user_id": "@clementine:test"}, {"observed_user_id": "@clementine:test", "accepted": True},
] ]
def get_presence_list(user_localpart, accepted=None): def get_presence_list(user_localpart, accepted=None):
return defer.succeed(self.presence_list) return defer.succeed(self.presence_list)
@ -144,8 +144,8 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
@defer.inlineCallbacks @defer.inlineCallbacks
def test_set_my_state(self): def test_set_my_state(self):
self.presence_list = [ self.presence_list = [
{"observed_user_id": "@banana:test"}, {"observed_user_id": "@banana:test", "accepted": True},
{"observed_user_id": "@clementine:test"}, {"observed_user_id": "@clementine:test", "accepted": True},
] ]
mocked_set = self.datastore.set_presence_state mocked_set = self.datastore.set_presence_state
@ -167,8 +167,8 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
self.mock_get_joined.side_effect = get_joined self.mock_get_joined.side_effect = get_joined
self.presence_list = [ self.presence_list = [
{"observed_user_id": "@banana:test"}, {"observed_user_id": "@banana:test", "accepted": True},
{"observed_user_id": "@clementine:test"}, {"observed_user_id": "@clementine:test", "accepted": True},
] ]
self.datastore.set_presence_state.return_value = defer.succeed( self.datastore.set_presence_state.return_value = defer.succeed(
@ -203,9 +203,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
"presence": ONLINE, "presence": ONLINE,
"last_active_ago": 0, "last_active_ago": 0,
"displayname": "Frank", "displayname": "Frank",
"avatar_url": "http://foo"}, "avatar_url": "http://foo",
"accepted": True},
{"observed_user": self.u_clementine, {"observed_user": self.u_clementine,
"presence": OFFLINE} "presence": OFFLINE,
"accepted": True}
], presence) ], presence)
self.mock_update_client.assert_has_calls([ self.mock_update_client.assert_has_calls([
@ -233,7 +235,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
@defer.inlineCallbacks @defer.inlineCallbacks
def test_push_remote(self): def test_push_remote(self):
self.presence_list = [ self.presence_list = [
{"observed_user_id": "@potato:remote"}, {"observed_user_id": "@potato:remote", "accepted": True},
] ]
self.datastore.set_presence_state.return_value = defer.succeed( self.datastore.set_presence_state.return_value = defer.succeed(

View File

@ -183,7 +183,7 @@ class PresenceListTestCase(unittest.TestCase):
@defer.inlineCallbacks @defer.inlineCallbacks
def test_get_my_list(self): def test_get_my_list(self):
self.datastore.get_presence_list.return_value = defer.succeed( self.datastore.get_presence_list.return_value = defer.succeed(
[{"observed_user_id": "@banana:test"}], [{"observed_user_id": "@banana:test", "accepted": True}],
) )
(code, response) = yield self.mock_resource.trigger("GET", (code, response) = yield self.mock_resource.trigger("GET",
@ -191,7 +191,7 @@ class PresenceListTestCase(unittest.TestCase):
self.assertEquals(200, code) self.assertEquals(200, code)
self.assertEquals([ self.assertEquals([
{"user_id": "@banana:test", "presence": OFFLINE}, {"user_id": "@banana:test", "presence": OFFLINE, "accepted": True},
], response) ], response)
self.datastore.get_presence_list.assert_called_with( self.datastore.get_presence_list.assert_called_with(