def _save_and_flush_and_response_when_ready(self): # need to deactivate cache, in case after_all_players_arrive # finished running after the moment set_attributes # was called in this request. # because in response_when_ready we will call # increment_index_in_pages, which does a look-ahead and calls # is_displayed() on the following pages. is_displayed() might # depend on a field that is set in after_all_players_arrive # so, need to clear the cache to ensure # that we get fresh data. # Note: i was never able to reproduce this myself -- just heard # from Anthony N. # and it shouldn't happen, because only the last player to visit # can set is_ready(). if there is a request coming after that, # then it must be someone refreshing the page manually. # i guess we should protect against that. # is_displayed() could also depend on a field on participant # that was set on the wait page, so need to refresh participant, # because it is passed as an arg to set_attributes(). otree.db.idmap.save_objects() idmap.flush() return self._response_when_ready()
def test_flush_db1(self): c1 = Category.objects.using('db1').get(pk=1) c2 = Category.objects.using('db2').get(pk=1) flush(db='db1') self.assertIsNot(c1, Category.objects.using('db1').get(pk=1)) self.assertIs(c2, Category.objects.using('db2').get(pk=1))
def deactivate_cache(): ''' The Idmap cache is always being populated, even if it's not "active" We just ignore its contents. It doesn't look like idmap has a way to turn it off entirely. ''' _toggle.is_active = False # 2017-08-07: flush both in activate and deactivate, just to be sure # i was getting some unexpected behavior in tests, when a test without # IDmap ran after a test with IDmap idmap.flush()
def test_flush_all(self): # make a list of Articles and SubArticles so that they're in cache list(Article.objects.all()) sub_pks = [sa.pk for sa in SubArticle.objects.all()] # should flush Article and SubArticle's caches flush() for pk in sub_pks: self.assertIsNone(SubArticle.get_cached_instance(pk))
def test_flush_all(self): c0 = Category.objects.get(pk=1) c1 = Category.objects.using('db1').get(pk=1) c2 = Category.objects.using('db2').get(pk=1) flush() self.assertIsNot(c0, Category.objects.get(pk=1)) self.assertIsNot(c1, Category.objects.using('db1').get(pk=1)) self.assertIsNot(c2, Category.objects.using('db2').get(pk=1))
def activate_cache(): idmap.flush() _toggle.is_active = True
def _pre_setup(self): super(TestCase, self)._pre_setup() flush()
def test_refresh_from_db_after_flush(self): flush() self.cat.refresh_from_db() cached_c = Category.get_cached_instance(pk=self.cat.pk) self.assertIsNotNone(cached_c)
def test_flat_values_list_after_flush(self): cat_pk = self.cat.pk flush() self.assertEqual( Category.objects.values_list('name', flat=True).get(pk=cat_pk), 'Category 0')
def inner_dispatch(self, *args, **kwargs): ## EARLY EXITS if self._was_completed(): return self._save_and_flush_and_response_when_ready() is_displayed = self.is_displayed() if self.group_by_arrival_time and not is_displayed: # in GBAT, either all players should skip a page, or none should. # we don't support some players skipping and others not. return self._response_when_ready() if is_displayed and not self.group_by_arrival_time: if self._get_unvisited_ids(): self.participant.is_on_wait_page = True return self._get_wait_page() ## END EARLY EXITS with get_redis_lock(name='otree_waitpage') or wait_page_thread_lock: # setting myself to _gbat_arrived = True should happen inside the lock # because otherwise, another player might be able to see that I have arrived # before I can run get_players_for_group, and they might end up grouping # me. But because I am not in their group, they will not run AAPA for me # so I will be considered 'already_grouped' and redirected to a wait page, # and AAPA will never be run. # also, it's simpler in general to have a broad lock. # and easier to explain to users that it will be run as each player # arrives. if self.group_by_arrival_time: self.player._gbat_arrived = True # _last_request_timestamp is already set in set_attributes, # but set it here just so we can guarantee self.participant._last_request_timestamp = time.time() # we call save_objects() below otree.db.idmap.save_objects() idmap.flush() # make a clean copy for GBAT and AAPA # self.player and self.participant etc are undefined # and no objects are cached inside it # and it doesn't affect the current instance wp = type(self)() # type: WaitPage wp.set_attributes_waitpage_clone(original_view=self) # needs to happen before calculating participant_pk_set # because this can change the group or PK if wp.group_by_arrival_time: wp._player_access_forbidden = Undefined_GetPlayersForGroup() wp._participant_access_forbidden = Undefined_GetPlayersForGroup() wp._group_access_forbidden = Undefined_GetPlayersForGroup() # check if the player was already grouped. # this is a 'check' of the check-then-act, so it needs to be # inside the lock. # It's possible that the player # was grouped, but the Completion object does not exist yet, # because aapa was not run. already_grouped = type(self.player).objects.filter( id=self.player.id).values_list( '_gbat_grouped', flat=True)[0] if already_grouped: regrouped = False else: regrouped = wp._gbat_try_to_regroup() if not regrouped: self.participant.is_on_wait_page = True return self._get_wait_page() wp._player_access_forbidden = None wp._participant_access_forbidden = None wp._group_access_forbidden = None # the group membership might be modified # in after_all_players_arrive, so calculate this first participant_pk_set = set( self._group_or_subsession.player_set .values_list('participant__pk', flat=True)) if not self._was_completed(): if is_displayed: # if any player can skip the wait page, # then we shouldn't run after_all_players_arrive # because if some players are able to proceed to the next page # before after_all_players_arrive is run, # then after_all_players_arrive is probably not essential. # often, there are some wait pages that all players skip, # because they should only be shown in certain rounds. # maybe the fields that after_all_players_arrive depends on # are null # something to think about: ideally, should we check if # all players skipped, or any player skipped? # as a shortcut, we just check if is_displayed is true # for the last player. wp._player_access_forbidden = Undefined_AfterAllPlayersArrive_Player() wp._participant_access_forbidden = Undefined_AfterAllPlayersArrive_Player() if wp.wait_for_all_groups: wp._group_access_forbidden = Undefined_AfterAllPlayersArrive_Group() else: wp._group_access_forbidden = None wp._group_for_aapa = self.group wp.after_all_players_arrive() # need to save to the results of after_all_players_arrive # to the DB, before sending the completion message to other players # this was causing a race condition on 2016-11-04 otree.db.idmap.save_objects() # even if this player skips the page and after_all_players_arrive # is not run, we need to indicate that the waiting players can advance self._mark_completed() self.send_completion_message(participant_pk_set) return self._save_and_flush_and_response_when_ready()