Contact: fumanchu@aminus.org

Log in as guest/dejavu to create tickets

Changeset 577

Show
Ignore:
Timestamp:
11/06/07 13:05:26
Author:
fumanchu
Message:

Crazycache: Test and fix for some indexing errors.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/crazycache/dejavu/storage/caching.py

    r575 r577  
    9494        # Try to retrieve units using a cached index. 
    9595        if cls.identifiers and cls in self.cache.classes: 
    96             units = self.cache.scan(self.nextstore, cls, expr) 
    97             if units is not None: 
    98                 units = [(unit,) for unit in units] 
    99                 for unit in self._paginate(units, order, limit, offset, single=True): 
    100                     yield unit 
    101                 return 
     96            if hasattr(self.cache, 'scan'): 
     97                units = self.cache.scan(self.nextstore, cls, expr) 
     98                if units is not None: 
     99                    units = [(unit,) for unit in units] 
     100                    for unit in self._paginate(units, order, limit, offset, 
     101                                               single=True): 
     102                        yield unit 
     103                    return 
    102104         
    103105        # Query storage. 
  • branches/crazycache/dejavu/storage/storememcached.py

    r575 r577  
    386386                unit = self.client.get(key) 
    387387                if unit is not None: 
     388                    unit.cleanse() 
    388389                    units.append(unit) 
    389390        return units 
     
    513514        keyattrs = self.primary_keys[cls] 
    514515         
    515         # Get a cached list of identifier-tuples. 
     516        # Find the best index for the given filters. 
    516517        for index in indexset: 
    517518            if set(filters.keys()) >= set(index): 
    518                 indexcriteria = dict([(k, filters[k]) for k in index]) 
    519519                break 
    520520        else: 
     
    522522            return None 
    523523         
    524         ids = indexset.get(indexcriteria) 
     524        criteria = [(k, filters[k]) for k in index] 
     525        ids = indexset.get(criteria) 
    525526        if ids is None: 
    526527            # Not in the cache. Grab the list of id-tuples from nextstore. 
    527             ids = mainstore.view((cls, keyattrs, filters)) 
     528            # Note well: we're NOT grabbing view(.., filters), because 
     529            # if filters > criteria, that would cache a subset of 
     530            # the index leaf node. 
     531            ids = mainstore.view((cls, keyattrs, dict(criteria))) 
    528532            # Then cache the list result for next time. Note that index 
    529533            # contents are unordered. 
    530             indexset.put(indexcriteria, ids, time=self.index_time) 
    531              
    532             # Query the cache for multiple units (by id). 
    533             units = indexset.scan(ids) 
    534             misses = [k for k in ids if k not in units] 
    535         else: 
    536             # Query the cache for multiple units (by id). 
    537             units = indexset.scan(ids) 
    538              
    539             # Remove any idents from the index node that no longer 
    540             # satisfy the index criteria. This is how we update 
    541             # index nodes--eager adds but late discards. 
    542             removals = False 
    543             for id, unit in units.items(): 
    544                 for key, value in filters.iteritems(): 
    545                     if getattr(unit, key) != value: 
    546                         removals = True 
    547                         del units[id] 
    548                         ids.remove(id) 
    549                         break 
    550             if removals: 
    551                 indexset.put(indexcriteria, ids, time=self.index_time) 
    552              
    553             misses = [k for k in ids if k not in units] 
     534            indexset.put(criteria, ids, time=self.index_time) 
     535         
     536        # Query the cache for multiple units (by id). 
     537        units = indexset.scan(ids) 
    554538         
    555539        # Now query the nextstore for any units that the cache missed... 
     540        misses = [k for k in ids if k not in units] 
    556541        if self.index_stride: 
    557542            # ...in chunks of length: self.index_stride. 
     
    580565                    pass 
    581566         
     567        indexset.filter(index, filters, units) 
     568         
    582569        return units.values() 
    583570 
     
    614601        return iter(self.indices) 
    615602     
    616     def key(self, filters): 
    617         """Return the cache key for the given filters. 
    618          
    619         If filters is an empty dict, the 'global index' key is returned. 
     603    def key(self, criteria): 
     604        """Return the cache key for the index node for the given criteria. 
     605         
     606        The given criteria must be an iterable of (key, value) tuples, 
     607        and must only contain keys for an existing index. 
     608         
     609        If criteria is an empty list, the 'global index' key is returned. 
    620610        """ 
    621611        criteria = ["%s=%s" % (k, str(v).replace(" ", "+")) 
    622                     for k, v in filters.iteritems()
     612                    for k, v in criteria
    623613        return self._key_template % ",".join(criteria) 
    624614     
    625     def get(self, filters): 
    626         """Return a cached list of unit ids which match the given filters dict. 
     615    def get(self, criteria): 
     616        """Return a cached list of unit ids which match the given criteria. 
     617         
     618        The given criteria must be an iterable of (key, value) tuples, 
     619        and must only contain keys for an existing index. 
     620         
     621        If criteria is an empty list, the 'global index' key is returned. 
    627622         
    628623        The ids returned will be a list of tuples of the form: 
    629624            tuple([getattr(unit, name) for name in primary_keys[cls]]) 
    630625        """ 
    631         cache_key = self.key(filters
     626        cache_key = self.key(criteria
    632627        ids = self.store.client.get(cache_key) 
    633628        if self.store.logflags & logflags.IO: 
     
    640635        return ids 
    641636     
    642     def put(self, filters, ids, time=0): 
    643         """Cache a list of unit identifiers which match the given filters dict. 
     637    def put(self, criteria, ids, time=0): 
     638        """Cache a list of unit identifiers which match the given criteria. 
     639         
     640        The given criteria must be an iterable of (key, value) tuples, 
     641        and must only contain keys for an existing index. 
     642         
     643        If criteria is an empty list, the 'global index' key is returned. 
    644644         
    645645        The ids provided MUST be a list of tuples of the form: 
    646646            tuple([getattr(unit, name) for name in primary_keys[cls]]) 
    647647        """ 
    648         cache_key = self.key(filters
     648        cache_key = self.key(criteria
    649649        if self.store.logflags & logflags.IO: 
    650650            self.store.log(logflags.IO.message("INDEX PUT (%s) len %r: %r" % 
     
    658658            tuple([getattr(unit, name) for name in primary_keys[cls]]) 
    659659         
    660         The returned dict will not contain entries for missed ids. 
     660        The returned dict will not contain entries for any units which 
     661        have expired from the cache. Callers may use this information 
     662        to request missed units from another store. 
    661663        """ 
    662664        clsname = self.cls.__name__ 
     
    671673                unit = data.get(k, None) 
    672674                if unit is not None: 
     675                    unit.cleanse() 
    673676                    units[i] = unit 
    674677        else: 
     
    694697            # to perform single gets against memcached, rather than the 
    695698            # get_multi calls that self.xrecall performs. 
    696             indexcriteria = dict([(k, filters[k]) for k in index]) 
    697             ids = self.get(indexcriteria) 
     699            criteria = [(k, filters[k]) for k in index] 
     700            ids = self.get(criteria) 
    698701            if ids: 
    699702                for id in ids: 
     
    722725        index, although it may and often should contain additional entries. 
    723726        """ 
    724         indexcriteria = dict([(k, filters[k]) for k in index]) 
    725         ids = self.get(indexcriteria) 
     727        criteria = [(k, filters[k]) for k in index] 
     728        ids = self.get(criteria) 
    726729        if ids: 
    727730            units = self.scan(ids) 
    728              
    729             removals = False 
    730             for id, unit in units.items(): 
    731                 for k, v in filters.iteritems(): 
    732                     if getattr(unit, k) != v: 
    733                         if k in index: 
    734                             removals = True 
    735                             del units[id] 
    736                             ids.remove(id) 
    737                         break 
    738                 else: 
    739                     unit.cleanse() 
    740                     yield unit 
    741              
    742             if removals: 
    743                 # Remove any idents from the index node that no longer 
    744                 # satisfy the index criteria. This is how we update 
    745                 # index nodes--eager adds but late discards. 
    746                 self.put(indexcriteria, ids, time=self.store.index_time) 
     731            self.filter(index, filters, units) 
     732            for unit in units.itervalues(): 
     733                unit.cleanse() 
     734                yield unit 
     735     
     736    def filter(self, index, filters, units): 
     737        """Remove any units which don't match filters, and update the index. 
     738         
     739        The filters argument must contain an entry for each key in the given 
     740        index, although it may and often should contain additional entries. 
     741         
     742        The 'units' arg must be a dict of (id, unit) pairs, and must be 
     743        the complete set of units from an index node; that is, the result 
     744        of an indexset.get() call for the same index. 
     745        """ 
     746        ids = units.keys() 
     747        removals = False 
     748        for id, unit in units.items(): 
     749            for key, value in filters.iteritems(): 
     750                if getattr(unit, key) != value: 
     751                    del units[id] 
     752                    # Remove any idents from the index node that no longer 
     753                    # satisfy the index criteria. This is how we update 
     754                    # index nodes--eager adds but late discards. 
     755                    if key in index: 
     756                        removals = True 
     757                        ids.remove(id) 
     758        if removals: 
     759            criteria = [(k, filters[k]) for k in index] 
     760            indexset.put(criteria, ids, time=self.store.index_time) 
    747761     
    748762    def add(self, unit): 
     
    751765                       for name in self.store.primary_keys[self.cls]]) 
    752766        for index in self.indices: 
    753             indexcriteria = dict([(k, getattr(unit, k)) for k in index]) 
    754             indexnode = self.get(indexcriteria) or [] 
     767            criteria = [(k, getattr(unit, k)) for k in index] 
     768            indexnode = self.get(criteria) or [] 
    755769            if ident not in indexnode: 
    756770                indexnode.append(ident) 
    757                 self.put(indexcriteria, indexnode) 
     771                self.put(criteria, indexnode) 
    758772     
    759773    def discard(self, unit): 
     
    762776                       for name in self.store.primary_keys[self.cls]]) 
    763777        for index in self.indices: 
    764             indexcriteria = dict([(k, getattr(unit, k)) for k in index]) 
    765             indexnode = self.get(indexcriteria) or [] 
     778            criteria = [(k, getattr(unit, k)) for k in index] 
     779            indexnode = self.get(criteria) or [] 
    766780            if ident in indexnode: 
    767781                indexnode.remove(ident) 
    768                 self.put(indexcriteria, indexnode) 
    769  
     782                self.put(criteria, indexnode) 
     783 
  • branches/crazycache/dejavu/test/zoo_fixture.py

    r567 r577  
    6262 
    6363class Animal(Unit): 
     64    Class = UnitProperty(index=True) 
    6465    Species = UnitProperty(hints={'bytes': 100}) 
    6566    ZooID = UnitProperty(int, index=True) 
     
    256257             
    257258            # Animals 
    258             leopard = Animal(Species='Leopard', Lifespan=73.5) 
     259            leopard = Animal(Class='Mammalia', Species='Leopard', Lifespan=73.5) 
    259260            self.assertEqual(leopard.PreviousZoos, None) 
    260261            box.memorize(leopard) 
     
    264265            leopard.LastEscape = datetime.datetime(2004, 12, 21, 8, 15, 0, 999907) 
    265266             
    266             lion = Animal(Species='Lion', ZooID=WAP.ID, 
     267            lion = Animal(Class='Mammalia', Species='Lion', ZooID=WAP.ID, 
    267268                          LastEscape = datetime.datetime(2007, 9, 24, 
    268269                                                         16, 18, 42)) 
    269270            box.memorize(lion) 
    270271             
    271             box.memorize(Animal(Species='Slug', Legs=1, Lifespan=.75, 
     272            box.memorize(Animal(Class='Gastropoda', Species='Slug', 
     273                                Legs=1, Lifespan=.75, 
    272274                                # Test our 8000-byte limit 
    273275                                PreviousZoos=["f" * (8000 - 14)])) 
    274276             
    275             tiger = Animal(Species='Tiger', PreviousZoos=['animal\\universe']) 
     277            tiger = Animal(Class='Mammalia', Species='Tiger', 
     278                           PreviousZoos=['animal\\universe']) 
    276279            box.memorize(tiger) 
    277280             
    278281            # Override Legs.default with itself just to make sure it works. 
    279             box.memorize(Animal(Species='Bear', Legs=4)) 
     282            box.memorize(Animal(Class='Mammalia', Species='Bear', Legs=4)) 
    280283            # Notice that ostrich.PreviousZoos is [], whereas leopard is None. 
    281             box.memorize(Animal(Species='Ostrich', Legs=2, PreviousZoos=[], 
     284            box.memorize(Animal(Class='Aves', Species='Ostrich', 
     285                                Legs=2, PreviousZoos=[], 
    282286                                Lifespan=103.2)) 
    283             box.memorize(Animal(Species='Centipede', Legs=100)) 
    284              
    285             emp = Animal(Species='Emperor Penguin', Legs=2) 
     287            box.memorize(Animal(Class='Chilopoda', Species='Centipede', Legs=100)) 
     288             
     289            emp = Animal(Class='Aves', Species='Emperor Penguin', Legs=2) 
    286290            box.memorize(emp) 
    287             adelie = Animal(Species='Adelie Penguin', Legs=2, 
     291            adelie = Animal(Class='Aves', Species='Adelie Penguin', Legs=2, 
    288292                            LastEscape = datetime.datetime(2007, 9, 20, 
    289293                                                           19, 10, 14)) 
     
    292296            seaworld.add(emp, adelie) 
    293297             
    294             millipede = Animal(Species='Millipede', Legs=1000000) 
     298            millipede = Animal(Class='Diplopoda', Species='Millipede', Legs=1000000) 
    295299            millipede.PreviousZoos = [WAP.Name] 
    296300            box.memorize(millipede) 
     
    299303             
    300304            # Add a mother and child to test relationships 
    301             bai_yun = Animal(Species='Ape', Legs=2) 
     305            bai_yun = Animal(Class='Mammalia', Species='Ape', Legs=2) 
    302306            box.memorize(bai_yun)   # ID = 11 
    303307            self.assertEqual(bai_yun.ID, 11) 
    304             hua_mei = Animal(Species='Ape', Legs=2, MotherID=bai_yun.ID) 
     308            hua_mei = Animal(Class='Mammalia', Species='Ape', Legs=2, 
     309                             MotherID=bai_yun.ID) 
    305310            box.memorize(hua_mei)   # ID = 12 
    306311            self.assertEqual(hua_mei.ID, 12) 
     
    539544        finally: 
    540545            box.flush_all() 
     546     
     547    def test_4a_Indexing(self): 
     548        # Different subsets of an index leaf node must return correct data. 
     549        for legs, species in [(2, set(['Ape', 'Ape'])), 
     550                              (4, set(['Leopard', 'Lion', 'Tiger', 'Bear']))]: 
     551            animals = root.recall(Animal, dict(Class='Mammalia', Legs=legs)) 
     552            self.assertEqual(set([a.Species for a in animals]), species) 
    541553     
    542554    def test_5_Aggregates(self):