Repository: mars-project/mars

Issue:
[BUG] md.concat `GraphContainsCycleError`
<!--
Thank you for your contribution!

Please review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.
-->

**Describe the bug**
There is a `GraphContainsCycleError` when concatenating two `DataFrame`.

**To Reproduce**
To help us reproducing this bug, please provide information below:
1. Python version: python 3.7.9
2. Mars version: 0.9.0
3. Minimized code:

```python
import mars
import mars.dataframe as md
import mars.tensor as mt

mars.new_session()

data = {"A": [i for i in range(10)]}
df0 = md.DataFrame(data)
df1 = df0[['A']]
df2 = df0[['A']]
df1 = df1.execute()
df2 = df2.execute()
df3 = md.concat([df1, df2], axis=1)
df3.execute()
```

4. Full stack of the error
```
Failed to run subtask PiMGCAxcMxbpDywbpJzkwKKR on band numa-0
Traceback (most recent call last):
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/scheduling/worker/execution.py", line 393, in internal_run_subtask
    subtask, band_name, subtask_api, batch_quota_req
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/scheduling/worker/execution.py", line 497, in _retry_run_subtask
    return await _retry_run(subtask, subtask_info, _run_subtask_once)
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/scheduling/worker/execution.py", line 107, in _retry_run
    raise ex
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/scheduling/worker/execution.py", line 69, in _retry_run
    return await target_async_func(*args)
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/scheduling/worker/execution.py", line 439, in _run_subtask_once
    return await asyncio.shield(aiotask)
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/subtask/api.py", line 69, in run_subtask_in_slot
    subtask
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/context.py", line 196, in send
    return self._process_result_message(result)
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/context.py", line 76, in _process_result_message
    raise message.as_instanceof_cause()
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/pool.py", line 586, in send
    result = await self._run_coro(message.message_id, coro)
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/pool.py", line 343, in _run_coro
    return await coro
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/api.py", line 120, in __on_receive__
    return await super().__on_receive__(message)
  File "mars/oscar/core.pyx", line 523, in __on_receive__
    raise ex
  File "mars/oscar/core.pyx", line 516, in mars.oscar.core._BaseActor.__on_receive__
    return await self._handle_actor_result(result)
  File "mars/oscar/core.pyx", line 401, in _handle_actor_result
    task_result = await coros[0]
  File "mars/oscar/core.pyx", line 444, in _run_actor_async_generator
    async with self._lock:
  File "mars/oscar/core.pyx", line 445, in mars.oscar.core._BaseActor._run_actor_async_generator
    with debug_async_timeout('actor_lock_timeout',
  File "mars/oscar/core.pyx", line 450, in mars.oscar.core._BaseActor._run_actor_async_generator
    res = await gen.athrow(*res)
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/subtask/worker/runner.py", line 125, in run_subtask
    result = yield self._running_processor.run(subtask)
  File "mars/oscar/core.pyx", line 455, in mars.oscar.core._BaseActor._run_actor_async_generator
    res = await self._handle_actor_result(res)
  File "mars/oscar/core.pyx", line 375, in _handle_actor_result
    result = await result
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/context.py", line 196, in send
    return self._process_result_message(result)
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/context.py", line 76, in _process_result_message
    raise message.as_instanceof_cause()
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/pool.py", line 586, in send
    result = await self._run_coro(message.message_id, coro)
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/pool.py", line 343, in _run_coro
    return await coro
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/api.py", line 120, in __on_receive__
    return await super().__on_receive__(message)
  File "mars/oscar/core.pyx", line 523, in __on_receive__
    raise ex
  File "mars/oscar/core.pyx", line 516, in mars.oscar.core._BaseActor.__on_receive__
    return await self._handle_actor_result(result)
  File "mars/oscar/core.pyx", line 401, in _handle_actor_result
    task_result = await coros[0]
  File "mars/oscar/core.pyx", line 444, in _run_actor_async_generator
    async with self._lock:
  File "mars/oscar/core.pyx", line 445, in mars.oscar.core._BaseActor._run_actor_async_generator
    with debug_async_timeout('actor_lock_timeout',
  File "mars/oscar/core.pyx", line 450, in mars.oscar.core._BaseActor._run_actor_async_generator
    res = await gen.athrow(*res)
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/subtask/worker/processor.py", line 625, in run
    result = yield self._running_aio_task
  File "mars/oscar/core.pyx", line 455, in mars.oscar.core._BaseActor._run_actor_async_generator
    res = await self._handle_actor_result(res)
  File "mars/oscar/core.pyx", line 375, in _handle_actor_result
    result = await result
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/subtask/worker/processor.py", line 441, in run
    chunk_graph = optimize(self._chunk_graph, self._engines)
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/optimization/physical/core.py", line 95, in optimize
    optimizer.optimize()
  File "/home/admin/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/optimization/physical/numexpr.py", line 105, in optimize
    for node in graph.topological_iter():
  File "mars/core/graph/core.pyx", line 438, in topological_iter
    raise GraphContainsCycleError
mars.core.graph.core.GraphContainsCycleError: [address=127.0.0.1:42161, pid=1937]
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 100.0/100 [00:00<00:00, 1238.79it/s]
---------------------------------------------------------------------------
GraphContainsCycleError                   Traceback (most recent call last)
<ipython-input-2-176c81d9f13a> in <module>
      8 df2.reset_index(inplace=True, drop=True)
      9 df3 = md.concat([df1, df2], axis=1)
---> 10 df3.execute()

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/core/entity/tileables.py in execute(self, session, **kw)
    460
    461     def execute(self, session=None, **kw):
--> 462         result = self.data.execute(session=session, **kw)
    463         if isinstance(result, TILEABLE_TYPE):
    464             return self

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/core/entity/executable.py in execute(self, session, **kw)
    142
    143         session = _get_session(self, session)
--> 144         return execute(self, session=session, **kw)
    145
    146     def _check_session(self, session: SessionType, action: str):

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/deploy/oscar/session.py in execute(tileable, session, wait, new_session_kwargs, show_progress, progress_update_interval, *tileables, **kwargs)
   1894         show_progress=show_progress,
   1895         progress_update_interval=progress_update_interval,
-> 1896         **kwargs,
   1897     )
   1898

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/deploy/oscar/session.py in execute(self, tileable, show_progress, warn_duplicated_execution, *tileables, **kwargs)
   1683         try:
   1684             execution_info: ExecutionInfo = fut.result(
-> 1685                 timeout=self._isolated_session.timeout
   1686             )
   1687         except KeyboardInterrupt:  # pragma: no cover

~/.pyenv/versions/3.7.9/lib/python3.7/concurrent/futures/_base.py in result(self, timeout)
    433                 raise CancelledError()
    434             elif self._state == FINISHED:
--> 435                 return self.__get_result()
    436             else:
    437                 raise TimeoutError()

~/.pyenv/versions/3.7.9/lib/python3.7/concurrent/futures/_base.py in __get_result(self)
    382     def __get_result(self):
    383         if self._exception:
--> 384             raise self._exception
    385         else:
    386             return self._result

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/deploy/oscar/session.py in _execute(session, wait, show_progress, progress_update_interval, cancelled, *tileables, **kwargs)
   1868             # set cancelled to avoid wait task leak
   1869             cancelled.set()
-> 1870         await execution_info
   1871     else:
   1872         return execution_info

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/deploy/oscar/session.py in wait()
    103
    104             async def wait():
--> 105                 return await self._aio_task
    106
    107             self._future_local.future = fut = asyncio.run_coroutine_threadsafe(

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/deploy/oscar/session.py in _run_in_background(self, tileables, task_id, progress, profiling)
    951                     )
    952                 if task_result.error:
--> 953                     raise task_result.error.with_traceback(task_result.traceback)
    954             if cancelled:
    955                 return

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/task/supervisor/processor.py in run(self)
    363             async with self._executor:
    364                 async for stage_args in self._iter_stage_chunk_graph():
--> 365                     await self._process_stage_chunk_graph(*stage_args)
    366         except Exception as ex:
    367             self.result.error = ex

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/task/supervisor/processor.py in _process_stage_chunk_graph(self, stage_id, stage_profiler, chunk_graph)
    242         with Timer() as timer:
    243             chunk_to_result = await self._executor.execute_subtask_graph(
--> 244                 stage_id, subtask_graph, chunk_graph, tile_context
    245             )
    246         stage_profiler.set("run", timer.duration)

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/task/execution/mars/executor.py in execute_subtask_graph(self, stage_id, subtask_graph, chunk_graph, tile_context, context)
    191         curr_tile_progress = self._tile_context.get_all_progress() - prev_progress
    192         self._stage_tile_progresses.append(curr_tile_progress)
--> 193         return await stage_processor.run()
    194
    195     async def __aexit__(self, exc_type, exc_val, exc_tb):

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/task/execution/mars/stage.py in run(self)
    238         if self.error_or_cancelled():
    239             if self.result.error is not None:
--> 240                 raise self.result.error.with_traceback(self.result.traceback)
    241             else:
    242                 raise asyncio.CancelledError()

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/scheduling/worker/execution.py in internal_run_subtask(self, subtask, band_name)
    391             logger.debug("Start actual running of subtask %s", subtask.subtask_id)
    392             subtask_info.result = await self._retry_run_subtask(
--> 393                 subtask, band_name, subtask_api, batch_quota_req
    394             )
    395             if remote_mapper_keys:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/scheduling/worker/execution.py in _retry_run_subtask(self, subtask, band_name, subtask_api, batch_quota_req)
    495         #  any exceptions occurred.
    496         if subtask.retryable:
--> 497             return await _retry_run(subtask, subtask_info, _run_subtask_once)
    498         else:
    499             try:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/scheduling/worker/execution.py in _retry_run(subtask, subtask_info, target_async_func, *args)
    105                 )
    106             else:
--> 107                 raise ex
    108
    109

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/scheduling/worker/execution.py in _retry_run(subtask, subtask_info, target_async_func, *args)
     67     while True:
     68         try:
---> 69             return await target_async_func(*args)
     70         except (OSError, MarsError) as ex:
     71             if subtask_info.num_retries < subtask_info.max_retries:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/scheduling/worker/execution.py in _run_subtask_once()
    437                     subtask_api.run_subtask_in_slot(band_name, slot_id, subtask)
    438                 )
--> 439                 return await asyncio.shield(aiotask)
    440             except asyncio.CancelledError as ex:
    441                 try:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/subtask/api.py in run_subtask_in_slot(self, band_name, slot_id, subtask)
     67         )
     68         return await ref.run_subtask.options(profiling_context=profiling_context).send(
---> 69             subtask
     70         )
     71

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/context.py in send(self, actor_ref, message, wait_response, profiling_context)
    194             if wait_response:
    195                 result = await self._wait(future, actor_ref.address, message)
--> 196                 return self._process_result_message(result)
    197             else:
    198                 return future

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/context.py in _process_result_message(message)
     74             return message.result
     75         else:
---> 76             raise message.as_instanceof_cause()
     77
     78     async def _wait(self, future: asyncio.Future, address: str, message: _MessageBase):

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/pool.py in send()
    584                 raise ActorNotExist(f"Actor {actor_id} does not exist")
    585             coro = self._actors[actor_id].__on_receive__(message.content)
--> 586             result = await self._run_coro(message.message_id, coro)
    587             processor.result = ResultMessage(
    588                 message.message_id,

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/pool.py in _run_coro()
    341         self._process_messages[message_id] = asyncio.tasks.current_task()
    342         try:
--> 343             return await coro
    344         finally:
    345             self._process_messages.pop(message_id, None)

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/api.py in __on_receive__()
    118             Message shall be (method_name,) + args + (kwargs,)
    119         """
--> 120         return await super().__on_receive__(message)
    121
    122

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in __on_receive__()
    521                 debug_logger.exception('Got unhandled error when handling message %.500r '
    522                                        'in actor %s at %s', message, self.uid, self.address)
--> 523             raise ex
    524
    525

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in mars.oscar.core._BaseActor.__on_receive__()
    514                 raise ValueError(f'call_method {call_method} not valid')
    515
--> 516             return await self._handle_actor_result(result)
    517         except Exception as ex:
    518             if _log_unhandled_errors:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in _handle_actor_result()
    399             # asyncio.wait as it introduces much overhead
    400             if len(coros) == 1:
--> 401                 task_result = await coros[0]
    402                 if extract_tuple:
    403                     result = task_result

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in _run_actor_async_generator()
    442             res = None
    443             while True:
--> 444                 async with self._lock:
    445                     with debug_async_timeout('actor_lock_timeout',
    446                                              'async_generator %r hold lock timeout', gen):

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in mars.oscar.core._BaseActor._run_actor_async_generator()
    443             while True:
    444                 async with self._lock:
--> 445                     with debug_async_timeout('actor_lock_timeout',
    446                                              'async_generator %r hold lock timeout', gen):
    447                         if not is_exception:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in mars.oscar.core._BaseActor._run_actor_async_generator()
    448                             res = await gen.asend(res)
    449                         else:
--> 450                             res = await gen.athrow(*res)
    451                 try:
    452                     if _log_cycle_send:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/subtask/worker/runner.py in run_subtask()
    123         try:
    124             self._running_processor = self._last_processor = processor
--> 125             result = yield self._running_processor.run(subtask)
    126         finally:
    127             self._running_processor = None

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in mars.oscar.core._BaseActor._run_actor_async_generator()
    453                         message_trace = pop_message_trace()
    454
--> 455                     res = await self._handle_actor_result(res)
    456                     is_exception = False
    457                 except:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in _handle_actor_result()
    373
    374         if inspect.isawaitable(result):
--> 375             result = await result
    376         elif is_async_generator(result):
    377             result = (result,)

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/context.py in send()
    194             if wait_response:
    195                 result = await self._wait(future, actor_ref.address, message)
--> 196                 return self._process_result_message(result)
    197             else:
    198                 return future

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/context.py in _process_result_message()
     74             return message.result
     75         else:
---> 76             raise message.as_instanceof_cause()
     77
     78     async def _wait(self, future: asyncio.Future, address: str, message: _MessageBase):

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/pool.py in send()
    584                 raise ActorNotExist(f"Actor {actor_id} does not exist")
    585             coro = self._actors[actor_id].__on_receive__(message.content)
--> 586             result = await self._run_coro(message.message_id, coro)
    587             processor.result = ResultMessage(
    588                 message.message_id,

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/backends/pool.py in _run_coro()
    341         self._process_messages[message_id] = asyncio.tasks.current_task()
    342         try:
--> 343             return await coro
    344         finally:
    345             self._process_messages.pop(message_id, None)

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/api.py in __on_receive__()
    118             Message shall be (method_name,) + args + (kwargs,)
    119         """
--> 120         return await super().__on_receive__(message)
    121
    122

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in __on_receive__()
    521                 debug_logger.exception('Got unhandled error when handling message %.500r '
    522                                        'in actor %s at %s', message, self.uid, self.address)
--> 523             raise ex
    524
    525

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in mars.oscar.core._BaseActor.__on_receive__()
    514                 raise ValueError(f'call_method {call_method} not valid')
    515
--> 516             return await self._handle_actor_result(result)
    517         except Exception as ex:
    518             if _log_unhandled_errors:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in _handle_actor_result()
    399             # asyncio.wait as it introduces much overhead
    400             if len(coros) == 1:
--> 401                 task_result = await coros[0]
    402                 if extract_tuple:
    403                     result = task_result

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in _run_actor_async_generator()
    442             res = None
    443             while True:
--> 444                 async with self._lock:
    445                     with debug_async_timeout('actor_lock_timeout',
    446                                              'async_generator %r hold lock timeout', gen):

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in mars.oscar.core._BaseActor._run_actor_async_generator()
    443             while True:
    444                 async with self._lock:
--> 445                     with debug_async_timeout('actor_lock_timeout',
    446                                              'async_generator %r hold lock timeout', gen):
    447                         if not is_exception:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in mars.oscar.core._BaseActor._run_actor_async_generator()
    448                             res = await gen.asend(res)
    449                         else:
--> 450                             res = await gen.athrow(*res)
    451                 try:
    452                     if _log_cycle_send:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/subtask/worker/processor.py in run()
    623         self._running_aio_task = asyncio.create_task(processor.run())
    624         try:
--> 625             result = yield self._running_aio_task
    626             logger.info("Finished subtask: %s", subtask.subtask_id)
    627             raise mo.Return(result)

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in mars.oscar.core._BaseActor._run_actor_async_generator()
    453                         message_trace = pop_message_trace()
    454
--> 455                     res = await self._handle_actor_result(res)
    456                     is_exception = False
    457                 except:

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/oscar/core.pyx in _handle_actor_result()
    373
    374         if inspect.isawaitable(result):
--> 375             result = await result
    376         elif is_async_generator(result):
    377             result = (result,)

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/services/subtask/worker/processor.py in run()
    439         try:
    440             raw_result_chunks = list(self._chunk_graph.result_chunks)
--> 441             chunk_graph = optimize(self._chunk_graph, self._engines)
    442             self._chunk_key_to_data_keys = get_chunk_key_to_data_keys(chunk_graph)
    443             report_progress = asyncio.create_task(self.report_progress_periodically())

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/optimization/physical/core.py in optimize()
     93         if not optimizer.is_available():
     94             continue
---> 95         optimizer.optimize()
     96
     97     return graph

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/optimization/physical/numexpr.py in optimize()
    103
    104         graph = self._graph
--> 105         for node in graph.topological_iter():
    106             if node.op.gpu or node.op.sparse:
    107                 # break

~/.pyenv/versions/3.7.9/envs/vir-gmars379/lib/python3.7/site-packages/mars/core/graph/core.pyx in topological_iter()
    436                     stack.append(succ)
    437         if len(visited) != len(self):
--> 438             raise GraphContainsCycleError

GraphContainsCycleError: [address=127.0.0.1:42161, pid=1937]
```

**Expected behavior**
A clear and concise description of what you expected to happen.

**Additional context**
Add any other context about the problem here.



Discussion:

Resolving patch:
diff --git a/mars/core/base.py b/mars/core/base.py
--- a/mars/core/base.py
+++ b/mars/core/base.py
@@ -12,10 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from functools import wraps
 from typing import Dict, Tuple, Type
 
-from ..serialization.core import Placeholder, fast_id
 from ..serialization.serializables import Serializable, StringField
 from ..serialization.serializables.core import SerializableSerializer
 from ..utils import tokenize
@@ -133,21 +131,7 @@ def to_kv(self, exclude_fields: Tuple[str], accept_value_types: Tuple[Type]):
         return kv
 
 
-def buffered_base(func):
-    @wraps(func)
-    def wrapped(self, obj: Base, context: Dict):
-        obj_id = (obj.key, obj.id)
-        if obj_id in context:
-            return Placeholder(fast_id(context[obj_id]))
-        else:
-            context[obj_id] = obj
-            return func(self, obj, context)
-
-    return wrapped
-
-
 class BaseSerializer(SerializableSerializer):
-    @buffered_base
     def serial(self, obj: Base, context: Dict):
         return super().serial(obj, context)
 
diff --git a/mars/utils.py b/mars/utils.py
--- a/mars/utils.py
+++ b/mars/utils.py
@@ -614,7 +614,6 @@ def build_fetch_chunk(chunk: ChunkType, **kwargs) -> ChunkType:
         is_broadcaster=chunk.is_broadcaster,
         kws=[params],
         _key=chunk.key,
-        _id=chunk.id,
         **kwargs,
     )
 


Test patch:
diff --git a/mars/deploy/oscar/tests/test_local.py b/mars/deploy/oscar/tests/test_local.py
--- a/mars/deploy/oscar/tests/test_local.py
+++ b/mars/deploy/oscar/tests/test_local.py
@@ -1141,3 +1141,37 @@ def test_naive_code_file():
             except psutil.NoSuchProcess:
                 pass
             raise
+
+
+@pytest.mark.parametrize("config", [{"backend": "mars"}])
+def test_fetch_concat(config):
+    session = new_session(
+        backend=config["backend"], n_cpu=2, web=False, use_uvloop=False
+    )
+    assert session is not None
+
+    with session:
+        data = {"A": [i for i in range(10)]}
+        df0 = md.DataFrame(data)
+        df1 = df0[["A"]]
+        df2 = df0[["A"]]
+        df1 = df1.execute()
+        df2 = df2.execute()
+        df3 = md.concat([df1, df2], axis=1)
+        ret = df3.execute()
+        df4 = ret.fetch()
+
+        pdf0 = pd.DataFrame(data)
+        pdf1 = pdf0[["A"]]
+        pdf2 = pdf0[["A"]]
+        pdf3 = pd.concat([pdf1, pdf2], axis=1)
+
+        assert pdf3.equals(df4)
+
+    for worker_pool in session._session.client._cluster._worker_pools:
+        _assert_storage_cleaned(
+            session.session_id, worker_pool.external_address, StorageLevel.MEMORY
+        )
+
+    session.stop_server()
+    assert get_default_async_session() is None