Merge lp:~stevenk/launchpad/bulk-eid-to-obj into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Approved by: Steve Kowalik
Approved revision: no longer in the source branch.
Merged at revision: 16687
Proposed branch: lp:~stevenk/launchpad/bulk-eid-to-obj
Merge into: lp:launchpad
Diff against target: 154 lines (+61/-24)
3 files modified
lib/lp/services/auditor/client.py (+8/-4)
lib/lp/services/enterpriseid.py (+23/-10)
lib/lp/services/tests/test_enterpriseid.py (+30/-10)
To merge this branch: bzr merge lp:~stevenk/launchpad/bulk-eid-to-obj
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+171705@code.launchpad.net

Commit message

Destroy enterpriseid_to_object for being terrible, its replacement is enterpriseids_to_objects.

Description of the change

Destroy enterpriseid_to_object for being terrible and issuing one query per eid passed to it. Its replacement, enterpriseids_to_objects will make one query per type of object.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

20 + eids = []
21 + for entry in logs['log-entries']:
22 + eids.extend([entry['actor'], entry['object']])

This should probably be a set; there will likely be many dupes.

76 +def enterpriseids_to_objects(eids):
77 + """Given a list of SOA Enterprise IDs, return a dict that maps the ID to
78 + its concrete object."""

That's a prolix and non-compliant docstring. "Dereference multiple SOA Enterprise IDs.", maybe?

79 + map_id_to_obj = {}
80 + obj_id_to_eid = defaultdict(dict)

Aren't those "eid_to_obj" and "dbid_to_eid"? map_id_to_obj also isn't needed until right at the end; I'd define it there for readability.

89 + type_ids[scheme[1]].append(int(scheme[2]))
90 + obj_id_to_eid[scheme[1]][int(scheme[2])] = eid

These are redundant. type_ids[foo] is just obj_id_to_eid[foo].keys(). Dropping type_ids also has the side-effect of eliminating dupes.

92 + for kind in types:

This could just iterate over obj_id_to_eid instead, to get just the types relevant to this query.

137 + def test_enterpriseids_to_objects(self):

This probably wants to also test for multiple classes.

review: Needs Fixing (code)
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/services/auditor/client.py'
--- lib/lp/services/auditor/client.py 2013-06-18 03:57:26 +0000
+++ lib/lp/services/auditor/client.py 2013-06-27 04:36:27 +0000
@@ -12,7 +12,7 @@
1212
13from lp.services.config import config13from lp.services.config import config
14from lp.services.enterpriseid import (14from lp.services.enterpriseid import (
15 enterpriseid_to_object,15 enterpriseids_to_objects,
16 object_to_enterpriseid,16 object_to_enterpriseid,
17 )17 )
1818
@@ -42,7 +42,11 @@
42 logs = super(AuditorClient, self).receive(42 logs = super(AuditorClient, self).receive(
43 obj, operation, actorobj, limit)43 obj, operation, actorobj, limit)
44 # Process the actors and objects back from enterprise ids.44 # Process the actors and objects back from enterprise ids.
45 for entry in logs['log-entries']:45 eids = set()
46 entry['actor'] = enterpriseid_to_object(entry['actor'])46 for entry in logs['log-entries']:
47 entry['object'] = enterpriseid_to_object(entry['object'])47 eids |= set([entry['actor'], entry['object']])
48 map_eids_to_obj = enterpriseids_to_objects(eids)
49 for entry in logs['log-entries']:
50 entry['actor'] = map_eids_to_obj.get(entry['actor'], None)
51 entry['object'] = map_eids_to_obj.get(entry['object'], None)
48 return logs['log-entries']52 return logs['log-entries']
4953
=== modified file 'lib/lp/services/enterpriseid.py'
--- lib/lp/services/enterpriseid.py 2012-07-26 22:12:53 +0000
+++ lib/lp/services/enterpriseid.py 2013-06-27 04:36:27 +0000
@@ -1,4 +1,4 @@
1# Copyright 2012 Canonical Ltd. This software is licensed under the1# Copyright 2012-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Enterprise ID utilities."""4"""Enterprise ID utilities."""
@@ -6,12 +6,14 @@
6__metaclass__ = type6__metaclass__ = type
7__all__ = [7__all__ = [
8 'object_to_enterpriseid',8 'object_to_enterpriseid',
9 'enterpriseid_to_object',9 'enterpriseids_to_objects',
10 ]10 ]
1111
12from collections import defaultdict
12import os13import os
1314
14from lp.services.config import config15from lp.services.config import config
16from lp.services.database.bulk import load
1517
1618
17def object_to_enterpriseid(obj):19def object_to_enterpriseid(obj):
@@ -25,17 +27,28 @@
25 return '%s:%s:%d' % (instance, otype, obj.id)27 return '%s:%s:%d' % (instance, otype, obj.id)
2628
2729
28def enterpriseid_to_object(eid):30def _known_types():
29 """Given an SOA Enterprise ID, return the object that it references."""
30 # Circular imports.31 # Circular imports.
31 from lp.registry.model.person import Person32 from lp.registry.model.person import Person
32 from lp.soyuz.model.queue import PackageUpload33 from lp.soyuz.model.queue import PackageUpload
33 scheme = eid.split(':')34 return {
34 if not scheme[0].startswith('lp'):
35 raise TypeError
36 known_types = {
37 'PackageUpload': PackageUpload,35 'PackageUpload': PackageUpload,
38 'Person': Person,36 'Person': Person,
39 }37 }
40 klass = known_types[scheme[1]]38
41 return klass.get(scheme[2])39
40def enterpriseids_to_objects(eids):
41 """Dereference multiple SOA Enterprise IDs."""
42 dbid_to_eid = defaultdict(dict)
43 for eid in eids:
44 if not eid.startswith('lp'):
45 raise TypeError
46 instance, cls, id = eid.split(':')
47 dbid_to_eid[cls][int(id)] = eid
48 types = _known_types()
49 eid_to_obj = {}
50 for kind in dbid_to_eid:
51 objs = load(types[kind], dbid_to_eid[kind].keys())
52 for obj in objs:
53 eid_to_obj[dbid_to_eid[kind][obj.id]] = obj
54 return eid_to_obj
4255
=== modified file 'lib/lp/services/tests/test_enterpriseid.py'
--- lib/lp/services/tests/test_enterpriseid.py 2012-03-05 02:24:17 +0000
+++ lib/lp/services/tests/test_enterpriseid.py 2013-06-27 04:36:27 +0000
@@ -1,20 +1,27 @@
1# Copyright 2012 Canonical Ltd. This software is licensed under the1# Copyright 2012-2013 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Test Enterprise ID utilities."""4"""Test Enterprise ID utilities."""
55
6__metaclass__ = type6__metaclass__ = type
77
8from testtools.matchers import Equals
9
10from lp.services.database.interfaces import IStore
8from lp.services.enterpriseid import (11from lp.services.enterpriseid import (
9 enterpriseid_to_object,12 enterpriseids_to_objects,
10 object_to_enterpriseid,13 object_to_enterpriseid,
11 )14 )
12from lp.testing import TestCaseWithFactory15from lp.testing import (
13from lp.testing.layers import DatabaseFunctionalLayer16 StormStatementRecorder,
17 TestCaseWithFactory,
18 )
19from lp.testing.layers import LaunchpadFunctionalLayer
20from lp.testing.matchers import HasQueryCount
1421
1522
16class TestEnterpriseId(TestCaseWithFactory):23class TestEnterpriseId(TestCaseWithFactory):
17 layer = DatabaseFunctionalLayer24 layer = LaunchpadFunctionalLayer
1825
19 def test_object_to_enterpriseid(self):26 def test_object_to_enterpriseid(self):
20 person = self.factory.makePerson()27 person = self.factory.makePerson()
@@ -22,8 +29,21 @@
22 expected = 'lp-development:Person:%d' % person.id29 expected = 'lp-development:Person:%d' % person.id
23 self.assertEqual(expected, eid)30 self.assertEqual(expected, eid)
2431
25 def test_enterpriseid_to_object(self):32 def test_enterpriseids_to_objects(self):
26 person = self.factory.makePerson()33 expected = {}
27 eid = 'lp-development:Person:%d' % person.id34 for x in range(5):
28 obj = enterpriseid_to_object(eid)35 person = self.factory.makePerson()
29 self.assertEqual(person, obj)36 upload = self.factory.makePackageUpload()
37 expected['lp-development:Person:%d' % person.id] = person
38 expected['lp-development:PackageUpload:%d' % upload.id] = upload
39 IStore(expected.values()[0].__class__).invalidate()
40 with StormStatementRecorder() as recorder:
41 objects = enterpriseids_to_objects(expected.keys())
42 self.assertThat(recorder, HasQueryCount(Equals(2)))
43 self.assertContentEqual(expected.keys(), objects.keys())
44 self.assertContentEqual(expected.values(), objects.values())
45
46 def test_enterpriseids_to_objects_non_existent_id(self):
47 objects = enterpriseids_to_objects(
48 ['lp-development:PackageUpload:10000'])
49 self.assertEqual({}, objects)