core (memory): entities cache improvments:

- entities cache is no more limited to bare jid
    - resources are now automatically updated in bare jid cache
This commit is contained in:
Goffi 2014-03-28 18:07:17 +01:00
commit 382d49a628
5 changed files with 118 additions and 70 deletions

View file

@ -320,8 +320,8 @@ class DbusObject(dbus.service.Object):
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
in_signature='s', out_signature='a{sa{s(sia{ss})}}',
async_callbacks=None)
def getPresenceStatus(self, profile_key="@DEFAULT@"):
return self._callback("getPresenceStatus", unicode(profile_key))
def getPresenceStatuses(self, profile_key="@DEFAULT@"):
return self._callback("getPresenceStatuses", unicode(profile_key))
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
in_signature='s', out_signature='s',

View file

@ -305,7 +305,7 @@ doc_param_0=contact_jid: jid of the contact
doc_param_1=%(doc_profile_key)s
doc_return=the last resource connected of the contact, or ""
[getPresenceStatus]
[getPresenceStatuses]
type=method
category=core
sig_in=s

View file

@ -40,6 +40,7 @@ class Const(object):
PROF_KEY_NONE = '@NONE@'
PROF_KEY_DEFAULT = '@DEFAULT@'
ENTITY_ALL = '@ALL@'
ENTITY_LAST_RESOURCE = 'last_resource'
## Configuration ##

View file

@ -115,7 +115,7 @@ class SAT(service.Service):
self.bridge.register("getContacts", self.getContacts)
self.bridge.register("getContactsFromGroup", self.getContactsFromGroup)
self.bridge.register("getLastResource", self.memory._getLastResource)
self.bridge.register("getPresenceStatus", self.memory.getPresenceStatus)
self.bridge.register("getPresenceStatuses", self.memory._getPresenceStatuses)
self.bridge.register("getWaitingSub", self.memory.getWaitingSub)
self.bridge.register("getWaitingConf", self.getWaitingConf)
self.bridge.register("sendMessage", self._sendMessage)

View file

@ -112,8 +112,8 @@ class Memory(object):
info(_("Memory manager init"))
self.initialized = defer.Deferred()
self.host = host
self.entitiesCache = {} # XXX: keep presence/last resource/other data in cache
# /!\ an entity is not necessarily in roster
self._entities_cache = {} # XXX: keep presence/last resource/other data in cache
# /!\ an entity is not necessarily in roster
self.subscriptions = {}
self.server_features = {} # used to store discovery's informations
self.server_identities = {}
@ -220,7 +220,7 @@ class Memory(object):
""""Iniatialise session for a profile
@param profile: %(doc_profile)s"""
info(_("[%s] Profile session started" % profile))
self.entitiesCache[profile] = {}
self._entities_cache[profile] = {}
def purgeProfileSession(self, profile):
"""Delete cache of data of profile
@ -228,7 +228,7 @@ class Memory(object):
info(_("[%s] Profile session purge" % profile))
self.params.purgeProfile(profile)
try:
del self.entitiesCache[profile]
del self._entities_cache[profile]
except KeyError:
error(_("Trying to purge roster status cache for a profile not in memory: [%s]") % profile)
@ -369,80 +369,118 @@ class Memory(object):
def _getLastResource(self, jid_s, profile_key):
jid_ = jid.JID(jid_s)
return self.getLastResource(jid_, profile_key)
return self.getLastResource(jid_, profile_key) or ""
def getLastResource(self, jid_, profile_key):
"""Return the last resource used by a jid_
@param jid_: bare jid
def getLastResource(self, entity_jid, profile_key):
"""Return the last resource used by an entity
@param entity_jid: entity jid
@param profile_key: %(doc_profile_key)s"""
profile = self.getProfileName(profile_key)
if not profile or not self.host.isConnected(profile):
error(_('Asking jid_s for a non-existant or not connected profile'))
return ""
entity = jid_.userhost()
if not entity in self.entitiesCache[profile]:
info(_("Entity not in cache"))
return ""
data = self.getEntityData(entity_jid.userhostJID(), [C.ENTITY_LAST_RESOURCE], profile_key)
try:
return self.entitiesCache[profile][entity]["last_resource"]
return data[C.ENTITY_LAST_RESOURCE]
except KeyError:
return ""
return None
def getPresenceStatus(self, profile_key):
def _getPresenceStatuses(self, profile_key):
ret = self.getPresenceStatuses(profile_key)
return {entity.full():data for entity, data in ret.iteritems()}
def getPresenceStatuses(self, profile_key):
"""Get all the presence status of a profile
@param profile_key: %(doc_profile_key)s
@return: presence data: key=entity JID, value=presence data for this entity
"""
profile = self.getProfileName(profile_key)
if not profile:
error(_('Asking contacts for a non-existant profile'))
return {}
raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile'))
entities_presence = {}
for entity in self.entitiesCache[profile]:
if "presence" in self.entitiesCache[profile][entity]:
entities_presence[entity] = self.entitiesCache[profile][entity]["presence"]
for entity in self._entities_cache[profile]:
if "presence" in self._entities_cache[profile][entity]:
entities_presence[entity] = self._entities_cache[profile][entity]["presence"]
debug("Memory getPresenceStatus (%s)", entities_presence)
return entities_presence
def setPresenceStatus(self, entity_jid, show, priority, statuses, profile_key):
"""Change the presence status of an entity"""
profile = self.getProfileName(profile_key)
if not profile:
error(_('Trying to add presence status to a non-existant profile'))
return
entity_data = self.entitiesCache[profile].setdefault(entity_jid.userhost(), {})
resource = jid.parse(entity_jid.full())[2] or ''
if resource:
entity_data["last_resource"] = resource
if not "last_resource" in entity_data:
entity_data["last_resource"] = ''
entity_data.setdefault("presence", {})[resource] = (show, priority, statuses)
def updateEntityData(self, entity_jid, key, value, profile_key):
"""Set a misc data for an entity
@param entity_jid: JID of the entity, or '@ALL@' to update all entities)
@param key: key to set (eg: "type")
@param value: value for this key (eg: "chatroom"), or C.PROF_KEY_NONE to delete
"""Change the presence status of an entity
@param entity_jid: jid.JID of the entity
@param show: show status
@param priotity: priotity
@param statuses: dictionary of statuses
@param profile_key: %(doc_profile_key)s
"""
profile = self.getProfileName(profile_key)
if not profile:
raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile'))
if not profile in self.entitiesCache:
entity_data = self._getEntitiesData(entity_jid, profile)[entity_jid]
resource = entity_jid.resource
if resource:
entity_data[C.ENTITY_LAST_RESOURCE] = resource
entity_data.setdefault("presence", {})[resource or ''] = (show, priority, statuses)
def _getEntitiesData(self, entity_jid, profile):
"""Get data dictionary for entities
@param entity_jid: JID of the entity, or C.ENTITY_ALL for all entities)
@param profile: %(doc_profile)s
@return: entities_data (key=jid, value=entity_data)
@raise: exceptions.ProfileNotInCacheError if profile is not in cache
"""
if not profile in self._entities_cache:
raise exceptions.ProfileNotInCacheError
if entity_jid == "@ALL@":
entities_map = self.entitiesCache[profile]
if entity_jid == C.ENTITY_ALL:
entities_data = self._entities_cache[profile]
else:
entity = entity_jid.userhost()
self.entitiesCache[profile].setdefault(entity, {})
entities_map = {entity: self.entitiesCache[profile][entity]}
for entity in entities_map:
entity_map = entities_map[entity]
if value == C.PROF_KEY_NONE and key in entity_map:
del entity_map[key]
entity_data = self._entities_cache[profile].setdefault(entity_jid, {})
entities_data = {entity_jid: entity_data}
return entities_data
def _updateEntityResources(self, entity_jid, profile):
"""Add a known resource to bare entity_jid cache
@param entity_jid: full entity_jid (with resource)
@param profile: %(doc_profile)s
"""
assert(entity_jid.resource)
entity_data = self._getEntitiesData(entity_jid.userhostJID(), profile)[entity_jid.userhostJID()]
resources = entity_data.setdefault('resources', set())
resources.add(entity_jid.resource)
def updateEntityData(self, entity_jid, key, value, profile_key):
"""Set a misc data for an entity
@param entity_jid: JID of the entity, or C.ENTITY_ALL to update all entities)
@param key: key to set (eg: "type")
@param value: value for this key (eg: "chatroom")
@param profile_key: %(doc_profile_key)s
"""
profile = self.getProfileName(profile_key)
if not profile:
raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile'))
entities_data = self._getEntitiesData(entity_jid, profile)
if entity_jid.resource and entity_jid != C.ENTITY_ALL:
self._updateEntityResources(entity_jid, profile)
for jid_ in entities_data:
entity_data = entities_data[jid_]
if value == C.PROF_KEY_NONE and key in entity_data:
del entity_data[key]
else:
entity_map[key] = value
entity_data[key] = value
if isinstance(value, basestring):
self.host.bridge.entityDataUpdated(entity, key, value, profile)
self.host.bridge.entityDataUpdated(jid_.full(), key, value, profile)
def delEntityData(self, entity_jid, key, profile_key):
"""Delete data for an entity
@param entity_jid: JID of the entity, or C.ENTITY_ALL to delete data from all entities)
@param key: key to delete (eg: "type")
@param profile_key: %(doc_profile_key)s
"""
entities_data = self._getEntitiesData(entity_jid, profile_key)
for entity_jid in entities_data:
entity_data = entities_data[entity_jid]
try:
del entity_data[key]
except KeyError:
debug("Key [%s] doesn't exist for [%s] in entities_cache" % (key, entity_jid.full()))
def getEntityData(self, entity_jid, keys_list, profile_key):
"""Get a list of cached values for entity
@ -453,16 +491,11 @@ class Memory(object):
if there is no value of a given key, resulting dict will
have nothing with that key nether
@raise: exceptions.UnknownEntityError if entity is not in cache
exceptions.ProfileNotInCacheError if profile is not in cache
"""
profile = self.getProfileName(profile_key)
if not profile:
raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile'))
if not profile in self.entitiesCache:
raise exceptions.ProfileNotInCacheError
if not entity_jid.userhost() in self.entitiesCache[profile]:
raise exceptions.UnknownEntityError(entity_jid.userhost())
entity_data = self.entitiesCache[profile][entity_jid.userhost()]
entity_data = self._getEntitiesData(entity_jid, profile)[entity_jid]
if not keys_list:
return entity_data
ret = {}
@ -471,15 +504,29 @@ class Memory(object):
ret[key] = entity_data[key]
return ret
def delEntityCache(self, entity_jid, profile_key):
def delEntityCache(self, entity_jid, delete_all_resources=True, profile_key=C.PROF_KEY_NONE):
"""Remove cached data for entity
@param entity_jid: JID of the entity
@param entity_jid: JID of the entity to delete
@param delete_all_resources: if True also delete all known resources form cache
@param profile_key: %(doc_profile_key)s
"""
profile = self.getProfileName(profile_key)
try:
del self.entitiesCache[profile][entity_jid.userhost()]
except KeyError:
pass
if not profile:
raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile'))
to_delete = set([entity_jid])
if delete_all_resources:
if entity_jid.resource:
raise ValueError(_("Need a bare jid to delete all resources"))
entity_data = self._getEntitiesData(entity_jid, profile)[entity_jid]
resources = entity_data.setdefault('resources', set())
to_delete.update([jid.JID("%s/%s" % (entity_jid.userhost(), resource)) for resource in resources])
for entity in to_delete:
try:
del self._entities_cache[profile][entity]
except KeyError:
debug("Can't delete entity [%s]: not in cache" % entity.full())
def addWaitingSub(self, type_, entity_jid, profile_key):
"""Called when a subcription request is received"""