comparison src/memory/memory.py @ 943:71926ec2114d

core (memory): entities cache improvments: - entities cache is no more limited to bare jid - resources are now automatically updated in bare jid cache
author Goffi <goffi@goffi.org>
date Fri, 28 Mar 2014 18:07:17 +0100
parents 5b2d2f1f05d0
children e1842ebcb2f3
comparison
equal deleted inserted replaced
942:598fc223cf59 943:71926ec2114d
110 110
111 def __init__(self, host): 111 def __init__(self, host):
112 info(_("Memory manager init")) 112 info(_("Memory manager init"))
113 self.initialized = defer.Deferred() 113 self.initialized = defer.Deferred()
114 self.host = host 114 self.host = host
115 self.entitiesCache = {} # XXX: keep presence/last resource/other data in cache 115 self._entities_cache = {} # XXX: keep presence/last resource/other data in cache
116 # /!\ an entity is not necessarily in roster 116 # /!\ an entity is not necessarily in roster
117 self.subscriptions = {} 117 self.subscriptions = {}
118 self.server_features = {} # used to store discovery's informations 118 self.server_features = {} # used to store discovery's informations
119 self.server_identities = {} 119 self.server_identities = {}
120 self.config = self.parseMainConf() 120 self.config = self.parseMainConf()
121 self.__fixLocalDir() 121 self.__fixLocalDir()
218 218
219 def startProfileSession(self, profile): 219 def startProfileSession(self, profile):
220 """"Iniatialise session for a profile 220 """"Iniatialise session for a profile
221 @param profile: %(doc_profile)s""" 221 @param profile: %(doc_profile)s"""
222 info(_("[%s] Profile session started" % profile)) 222 info(_("[%s] Profile session started" % profile))
223 self.entitiesCache[profile] = {} 223 self._entities_cache[profile] = {}
224 224
225 def purgeProfileSession(self, profile): 225 def purgeProfileSession(self, profile):
226 """Delete cache of data of profile 226 """Delete cache of data of profile
227 @param profile: %(doc_profile)s""" 227 @param profile: %(doc_profile)s"""
228 info(_("[%s] Profile session purge" % profile)) 228 info(_("[%s] Profile session purge" % profile))
229 self.params.purgeProfile(profile) 229 self.params.purgeProfile(profile)
230 try: 230 try:
231 del self.entitiesCache[profile] 231 del self._entities_cache[profile]
232 except KeyError: 232 except KeyError:
233 error(_("Trying to purge roster status cache for a profile not in memory: [%s]") % profile) 233 error(_("Trying to purge roster status cache for a profile not in memory: [%s]") % profile)
234 234
235 def save_xml(self, filename=None): 235 def save_xml(self, filename=None):
236 """Save parameters template to xml file""" 236 """Save parameters template to xml file"""
367 warning(_("Features of %s not available, maybe they haven't been asked yet?") % jid_) 367 warning(_("Features of %s not available, maybe they haven't been asked yet?") % jid_)
368 return None 368 return None
369 369
370 def _getLastResource(self, jid_s, profile_key): 370 def _getLastResource(self, jid_s, profile_key):
371 jid_ = jid.JID(jid_s) 371 jid_ = jid.JID(jid_s)
372 return self.getLastResource(jid_, profile_key) 372 return self.getLastResource(jid_, profile_key) or ""
373 373
374 374
375 def getLastResource(self, jid_, profile_key): 375 def getLastResource(self, entity_jid, profile_key):
376 """Return the last resource used by a jid_ 376 """Return the last resource used by an entity
377 @param jid_: bare jid 377 @param entity_jid: entity jid
378 @param profile_key: %(doc_profile_key)s""" 378 @param profile_key: %(doc_profile_key)s"""
379 profile = self.getProfileName(profile_key) 379 data = self.getEntityData(entity_jid.userhostJID(), [C.ENTITY_LAST_RESOURCE], profile_key)
380 if not profile or not self.host.isConnected(profile): 380 try:
381 error(_('Asking jid_s for a non-existant or not connected profile')) 381 return data[C.ENTITY_LAST_RESOURCE]
382 return ""
383 entity = jid_.userhost()
384 if not entity in self.entitiesCache[profile]:
385 info(_("Entity not in cache"))
386 return ""
387 try:
388 return self.entitiesCache[profile][entity]["last_resource"]
389 except KeyError: 382 except KeyError:
390 return "" 383 return None
391 384
392 def getPresenceStatus(self, profile_key): 385 def _getPresenceStatuses(self, profile_key):
393 profile = self.getProfileName(profile_key) 386 ret = self.getPresenceStatuses(profile_key)
394 if not profile: 387 return {entity.full():data for entity, data in ret.iteritems()}
395 error(_('Asking contacts for a non-existant profile')) 388
396 return {} 389 def getPresenceStatuses(self, profile_key):
390 """Get all the presence status of a profile
391 @param profile_key: %(doc_profile_key)s
392 @return: presence data: key=entity JID, value=presence data for this entity
393 """
394 profile = self.getProfileName(profile_key)
395 if not profile:
396 raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile'))
397 entities_presence = {} 397 entities_presence = {}
398 for entity in self.entitiesCache[profile]: 398 for entity in self._entities_cache[profile]:
399 if "presence" in self.entitiesCache[profile][entity]: 399 if "presence" in self._entities_cache[profile][entity]:
400 entities_presence[entity] = self.entitiesCache[profile][entity]["presence"] 400 entities_presence[entity] = self._entities_cache[profile][entity]["presence"]
401 401
402 debug("Memory getPresenceStatus (%s)", entities_presence) 402 debug("Memory getPresenceStatus (%s)", entities_presence)
403 return entities_presence 403 return entities_presence
404 404
405 def setPresenceStatus(self, entity_jid, show, priority, statuses, profile_key): 405 def setPresenceStatus(self, entity_jid, show, priority, statuses, profile_key):
406 """Change the presence status of an entity""" 406 """Change the presence status of an entity
407 profile = self.getProfileName(profile_key) 407 @param entity_jid: jid.JID of the entity
408 if not profile: 408 @param show: show status
409 error(_('Trying to add presence status to a non-existant profile')) 409 @param priotity: priotity
410 return 410 @param statuses: dictionary of statuses
411 entity_data = self.entitiesCache[profile].setdefault(entity_jid.userhost(), {}) 411 @param profile_key: %(doc_profile_key)s
412 resource = jid.parse(entity_jid.full())[2] or '' 412 """
413 profile = self.getProfileName(profile_key)
414 if not profile:
415 raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile'))
416 entity_data = self._getEntitiesData(entity_jid, profile)[entity_jid]
417 resource = entity_jid.resource
413 if resource: 418 if resource:
414 entity_data["last_resource"] = resource 419 entity_data[C.ENTITY_LAST_RESOURCE] = resource
415 if not "last_resource" in entity_data: 420 entity_data.setdefault("presence", {})[resource or ''] = (show, priority, statuses)
416 entity_data["last_resource"] = '' 421
417 422 def _getEntitiesData(self, entity_jid, profile):
418 entity_data.setdefault("presence", {})[resource] = (show, priority, statuses) 423 """Get data dictionary for entities
424 @param entity_jid: JID of the entity, or C.ENTITY_ALL for all entities)
425 @param profile: %(doc_profile)s
426 @return: entities_data (key=jid, value=entity_data)
427 @raise: exceptions.ProfileNotInCacheError if profile is not in cache
428 """
429 if not profile in self._entities_cache:
430 raise exceptions.ProfileNotInCacheError
431 if entity_jid == C.ENTITY_ALL:
432 entities_data = self._entities_cache[profile]
433 else:
434 entity_data = self._entities_cache[profile].setdefault(entity_jid, {})
435 entities_data = {entity_jid: entity_data}
436 return entities_data
437
438 def _updateEntityResources(self, entity_jid, profile):
439 """Add a known resource to bare entity_jid cache
440 @param entity_jid: full entity_jid (with resource)
441 @param profile: %(doc_profile)s
442 """
443 assert(entity_jid.resource)
444 entity_data = self._getEntitiesData(entity_jid.userhostJID(), profile)[entity_jid.userhostJID()]
445 resources = entity_data.setdefault('resources', set())
446 resources.add(entity_jid.resource)
419 447
420 def updateEntityData(self, entity_jid, key, value, profile_key): 448 def updateEntityData(self, entity_jid, key, value, profile_key):
421 """Set a misc data for an entity 449 """Set a misc data for an entity
422 @param entity_jid: JID of the entity, or '@ALL@' to update all entities) 450 @param entity_jid: JID of the entity, or C.ENTITY_ALL to update all entities)
423 @param key: key to set (eg: "type") 451 @param key: key to set (eg: "type")
424 @param value: value for this key (eg: "chatroom"), or C.PROF_KEY_NONE to delete 452 @param value: value for this key (eg: "chatroom")
425 @param profile_key: %(doc_profile_key)s 453 @param profile_key: %(doc_profile_key)s
426 """ 454 """
427 profile = self.getProfileName(profile_key) 455 profile = self.getProfileName(profile_key)
428 if not profile: 456 if not profile:
429 raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile')) 457 raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile'))
430 if not profile in self.entitiesCache: 458 entities_data = self._getEntitiesData(entity_jid, profile)
431 raise exceptions.ProfileNotInCacheError 459 if entity_jid.resource and entity_jid != C.ENTITY_ALL:
432 if entity_jid == "@ALL@": 460 self._updateEntityResources(entity_jid, profile)
433 entities_map = self.entitiesCache[profile] 461
434 else: 462 for jid_ in entities_data:
435 entity = entity_jid.userhost() 463 entity_data = entities_data[jid_]
436 self.entitiesCache[profile].setdefault(entity, {}) 464 if value == C.PROF_KEY_NONE and key in entity_data:
437 entities_map = {entity: self.entitiesCache[profile][entity]} 465 del entity_data[key]
438 for entity in entities_map:
439 entity_map = entities_map[entity]
440 if value == C.PROF_KEY_NONE and key in entity_map:
441 del entity_map[key]
442 else: 466 else:
443 entity_map[key] = value 467 entity_data[key] = value
444 if isinstance(value, basestring): 468 if isinstance(value, basestring):
445 self.host.bridge.entityDataUpdated(entity, key, value, profile) 469 self.host.bridge.entityDataUpdated(jid_.full(), key, value, profile)
470
471 def delEntityData(self, entity_jid, key, profile_key):
472 """Delete data for an entity
473 @param entity_jid: JID of the entity, or C.ENTITY_ALL to delete data from all entities)
474 @param key: key to delete (eg: "type")
475 @param profile_key: %(doc_profile_key)s
476 """
477 entities_data = self._getEntitiesData(entity_jid, profile_key)
478 for entity_jid in entities_data:
479 entity_data = entities_data[entity_jid]
480 try:
481 del entity_data[key]
482 except KeyError:
483 debug("Key [%s] doesn't exist for [%s] in entities_cache" % (key, entity_jid.full()))
446 484
447 def getEntityData(self, entity_jid, keys_list, profile_key): 485 def getEntityData(self, entity_jid, keys_list, profile_key):
448 """Get a list of cached values for entity 486 """Get a list of cached values for entity
449 @param entity_jid: JID of the entity 487 @param entity_jid: JID of the entity
450 @param keys_list: list of keys to get, empty list for everything 488 @param keys_list: list of keys to get, empty list for everything
451 @param profile_key: %(doc_profile_key)s 489 @param profile_key: %(doc_profile_key)s
452 @return: dict withs values for each key in keys_list. 490 @return: dict withs values for each key in keys_list.
453 if there is no value of a given key, resulting dict will 491 if there is no value of a given key, resulting dict will
454 have nothing with that key nether 492 have nothing with that key nether
455 @raise: exceptions.UnknownEntityError if entity is not in cache 493 @raise: exceptions.UnknownEntityError if entity is not in cache
456 exceptions.ProfileNotInCacheError if profile is not in cache
457 """ 494 """
458 profile = self.getProfileName(profile_key) 495 profile = self.getProfileName(profile_key)
459 if not profile: 496 if not profile:
460 raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile')) 497 raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile'))
461 if not profile in self.entitiesCache: 498 entity_data = self._getEntitiesData(entity_jid, profile)[entity_jid]
462 raise exceptions.ProfileNotInCacheError
463 if not entity_jid.userhost() in self.entitiesCache[profile]:
464 raise exceptions.UnknownEntityError(entity_jid.userhost())
465 entity_data = self.entitiesCache[profile][entity_jid.userhost()]
466 if not keys_list: 499 if not keys_list:
467 return entity_data 500 return entity_data
468 ret = {} 501 ret = {}
469 for key in keys_list: 502 for key in keys_list:
470 if key in entity_data: 503 if key in entity_data:
471 ret[key] = entity_data[key] 504 ret[key] = entity_data[key]
472 return ret 505 return ret
473 506
474 def delEntityCache(self, entity_jid, profile_key): 507 def delEntityCache(self, entity_jid, delete_all_resources=True, profile_key=C.PROF_KEY_NONE):
475 """Remove cached data for entity 508 """Remove cached data for entity
476 @param entity_jid: JID of the entity 509 @param entity_jid: JID of the entity to delete
477 """ 510 @param delete_all_resources: if True also delete all known resources form cache
478 profile = self.getProfileName(profile_key) 511 @param profile_key: %(doc_profile_key)s
479 try: 512 """
480 del self.entitiesCache[profile][entity_jid.userhost()] 513 profile = self.getProfileName(profile_key)
481 except KeyError: 514 if not profile:
482 pass 515 raise exceptions.ProfileUnknownError(_('Trying to get entity data for a non-existant profile'))
516 to_delete = set([entity_jid])
517
518 if delete_all_resources:
519 if entity_jid.resource:
520 raise ValueError(_("Need a bare jid to delete all resources"))
521 entity_data = self._getEntitiesData(entity_jid, profile)[entity_jid]
522 resources = entity_data.setdefault('resources', set())
523 to_delete.update([jid.JID("%s/%s" % (entity_jid.userhost(), resource)) for resource in resources])
524
525 for entity in to_delete:
526 try:
527 del self._entities_cache[profile][entity]
528 except KeyError:
529 debug("Can't delete entity [%s]: not in cache" % entity.full())
483 530
484 def addWaitingSub(self, type_, entity_jid, profile_key): 531 def addWaitingSub(self, type_, entity_jid, profile_key):
485 """Called when a subcription request is received""" 532 """Called when a subcription request is received"""
486 profile = self.getProfileName(profile_key) 533 profile = self.getProfileName(profile_key)
487 assert profile 534 assert profile