Repository: facebookresearch/ParlAI

Issue:
Double mturk world initialization
Very rarely it appears my Mturk task world is initialized twice for the same MTurk agent. I have experienced this myself when testing. Every mturk_agent.observe would just appear twice.

Even though this is my command:
python run.py -nc 2 -r 1.25 --live --allowed-conversations 1 --max-connections 1 --unique

So a worker should not be able to open 2 HITs.

I haven't seen any pattern to it and from my glance it appears that it might be a bug with ParlAI? I'd be very happy if you could help me fix this.

I used the qa_data_collection task as my foundation.

This is my run.py:

```
def main():
    argparser = ParlaiParser(False, False)
    argparser.add_parlai_data_path()
    argparser.add_mturk_args()
    opt = argparser.parse_args()
    opt['task'] = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
    opt.update(task_config)

    # Initialize a SQuAD teacher agent, which we will get context from
    task_opt = opt.copy()
    task_opt['datatype'] = 'train'
    task_opt['datapath'] = opt['datapath']

    mturk_agent_id = 'Worker'
    mturk_manager = MTurkManager(
        use_db=True,
        opt=opt,
        mturk_agent_ids=[mturk_agent_id]
    )

    mturk_manager.setup_server()

    try:
        mturk_manager.start_new_run()
        mturk_manager.ready_to_accept_workers()

        # agent_qualifications = [{
        #    'QualificationTypeId': '00000000000000000071',
        #    'Comparator': 'In',
        #    'LocaleValues': [{'Country': 'DE'}, {'Country': 'CH'}]
        # }]

        agent_qualifications = [{
                'QualificationTypeId': '00000000000000000071', 
                'Comparator':"NotEqualTo",
                'LocaleValues':[{'Country':"US", 'Subdivision':"CA"}]
        }]
        mturk_manager.create_hits(qualifications=agent_qualifications)

        used_workers = block_workers(mturk_manager)

        def check_workers_eligibility(workers):
            return workers

        eligibility_function = {
            'func': check_workers_eligibility,
            'multiple': True,
        }

        def assign_worker_roles(worker):
            worker[0].id = mturk_agent_id

        global run_conversation

        def run_conversation(mturk_manager, opt, workers):
            mturk_agent = workers[0]
            world = ScaffoldDataCollectionWorld(
                opt=opt,
                mturk_agent=mturk_agent
            )
            while not world.episode_done():
                    world.parley()
            used_workers.append(world.mturk_agent.worker_id)
            world.shutdown()
            world.review_work()

        mturk_manager.start_task(
            eligibility_function=eligibility_function,
            assign_role_function=assign_worker_roles,
            task_function=run_conversation
        )
    except BaseException:
        save_used_mturk_agent_ids(used_workers)
        raise
    finally:
        print("Shutting down TG1")
        save_used_mturk_agent_ids(used_workers)
        mturk_manager.expire_all_unassigned_hits()
        mturk_manager.shutdown()

if __name__ == '__main__':
    main()
```




Discussion:
Hi @nihiluis - this is indeed a bug that has been discovered on the platform, though I haven't confirmed the source just yet. 

I have however been able to create malformed states that cause this to occur. There's a potential fix based on resolving the state inconsistency - line 542 of MTurkManager, add the following line:
```
conversation_id = agent.conversation_id
```
I haven't been able to confirm that this prevents the degenerate state, but it's my current working theory. I haven't yet been able to reproduce the but with this line. 

If you choose to make this change locally, let me know if you run into the bug at any point afterwards.
Thank you for your quick response!

Are you sure about L542? L542 is just above the initialization of agent (at the db_logger check). Can I directly put it after the initialization?

```python
            if self.db_logger is not None:
                self.db_logger.log_worker_accept_assignment(
                    worker_id, assign_id, hit_id)
            agent = self.worker_manager._get_agent(worker_id, assign_id)
self._onboard_new_agent(agent)
```

https://github.com/facebookresearch/ParlAI/blob/master/parlai/mturk/core/mturk_manager.py#L542

Ah I copied that from my notes and the line numbers have changed since.

It should be around the `agent.log_reconnect()` call in the else block

Resolving patch:
diff --git a/parlai/mturk/core/mturk_manager.py b/parlai/mturk/core/mturk_manager.py
--- a/parlai/mturk/core/mturk_manager.py
+++ b/parlai/mturk/core/mturk_manager.py
@@ -549,6 +549,7 @@ def _on_alive(self, pkt):
             agent = self.worker_manager._get_agent(worker_id, assign_id)
             agent.log_reconnect()
             agent.alived = True
+            conversation_id = agent.conversation_id
             if agent.get_status() == AssignState.STATUS_NONE:
                 # See if assigned an onboarding world, update state if so
                 if self.is_onboarding_world(conversation_id):


Test patch: