<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="373045" data-post-id="373045">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="slouchpie" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/slouchpie/120/34819_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  slouchpie
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I am going to supply a rough draft to workaround the async live view functions problem.</p>
<p>Hefty disclaimer: this is a hacky workaround and not an elegant solution. The need for this workaround should probably be interpreted as a sign to re-consider the entire approach.</p>
<p>With that said, I am doing this in one of my personal projects.</p>
<p>OK, here we go.</p>
<p>Make this new module:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyAppWeb.Utils.LiveViewAsyncUtils do
  @moduledoc """
  Override the `assign_async` and `start_async`functions from `Phoenix.LiveView`

  We do this to allow checking out the `auto` mode Ecto Sandbox in tests.
  """

  alias Ecto.Adapters.SQL.Sandbox
  alias MyApp.Repo
  alias Phoenix.LiveView.Async

  def assign_async_aug(socket, key_or_keys, func, opts \\ []) do
    func_aug = build_augmented_func(func)
    # credo:disable-for-lines:1 MyApp.CustomCredo.Check.Design.NoLiveViewAsyncFunctions
    Async.assign_async(socket, key_or_keys, func_aug, opts)
  end

  def start_async_aug(socket, key_or_keys, func, opts \\ []) do
    func_aug = build_augmented_func(func)
    # credo:disable-for-lines:1 MyApp.CustomCredo.Check.Design.NoLiveViewAsyncFunctions
    Async.start_async(socket, key_or_keys, func_aug, opts)
  end

  defp build_augmented_func(func) do
    parent_pid = self()

    fn -&gt;
      Sandbox.allow(Repo, parent_pid, self())
      func.()
    end
  end
end
</code></pre>
<p>then add this to html_helpers function in my_app_web.ex</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  import MyAppWeb.Utils.LiveViewAsyncUtils
</code></pre>
<p>and then add this credo rule to .credo.exs</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">          {MyApp.CustomCredo.Check.Design.NoLiveViewAsyncFunctions},
</code></pre>
<p>and then add that new credo check somewhere in your project</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyApp.CustomCredo.Check.Design.NoLiveViewAsyncFunctions do
  @moduledoc false
  use Credo.Check,
    base_priority: :high,
    category: :design,
    explanations: [
      check: """
      This check ensures that `assign_async` and `start_async` from Phoenix.LiveView.Async
      are not used directly in the codebase.

      These functions are disallowed because they do not work correctly
      in async tests when Sandbox is in auto mode.
      """
    ]

  @doc false
  @impl true
  def run(%SourceFile{} = source_file, params) do
    issue_meta = IssueMeta.for(source_file, params)

    Credo.Code.prewalk(source_file, &amp;traverse(&amp;1, &amp;2, issue_meta))
  end

  # Match full module path: Phoenix.LiveView.Async.assign_async/start_async
  defp traverse(
         {:., meta, [{:__aliases__, _, [Phoenix, LiveView, Async]}, func]} = ast,
         issues,
         issue_meta
       )
       when func in [:assign_async, :start_async] do
    {ast, [issue_for(issue_meta, func, meta[:line]) | issues]}
  end

  # Match aliased form: Async.assign_async/start_async
  defp traverse({:., meta, [{:__aliases__, _, [Async]}, func]} = ast, issues, issue_meta)
       when func in [:assign_async, :start_async] do
    {ast, [issue_for(issue_meta, func, meta[:line]) | issues]}
  end

  # Match direct function calls: assign_async/start_async (imported)
  defp traverse({func, meta, _args} = ast, issues, issue_meta)
       when func in [:assign_async, :start_async] do
    {ast, [issue_for(issue_meta, func, meta[:line]) | issues]}
  end

  defp traverse(ast, issues, _issue_meta) do
    {ast, issues}
  end

  defp issue_for(issue_meta, function_name, line_no) do
    format_issue(
      issue_meta,
      message:
        "Found disallowed call to Phoenix.LiveView.Async.#{function_name}/3. Use `MyAppWeb.Utils.LiveViewAsyncUtils.#{function_name}_aug/3` instead",
      trigger: "#{function_name}",
      line_no: line_no
    )
  end
end
</code></pre>
<p>and finally run <code>mix credo</code> to see what needs fixing.</p>
<p>I am not recommending that anybody do this. I only provide the information because I feel responsible for people who followed the OP.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="373045" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/running-a-liveview-test-with-async-true/68765/23">Post #22</a>
	                </div>
	            </div>
              <div id="likers-container-373045" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="373045"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #22"></div>
  </section>
</div>
    <div class="postbit" id="373055" data-post-id="373055">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="LostKobrakai" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/LostKobrakai/120/3072_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  LostKobrakai
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Allowance can also be shared through caller tracking, which <code>Tasks</code> implement:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule TrialTest do
  use Async.DataCase, async: true

  test "b" do
    IO.inspect("Start a")

    Task.async(fn -&gt;
      {:ok, %{rows: [row]}} = Repo.query("SELECT txid_current()")
      IO.inspect(row, label: "a")
    end)

    Process.sleep(10000)
    IO.inspect("Stop a")
  end
end

defmodule Trial2Test do
  use Async.DataCase, async: true

  test "b" do
    IO.inspect("Start b")

    Task.async(fn -&gt;
      {:ok, %{rows: [row]}} = Repo.query("SELECT txid_current()")
      IO.inspect(row, label: "b")
    end)

    Process.sleep(10000)
    IO.inspect("Stop b")
  end
end
</code></pre>
<p>Running that results in the following. No error and wrapped in distinct transactions.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">"Start a"
a: [287939]
"Start b"
b: [287940]
"Stop a"
."Stop b".
</code></pre>
<p>The same mechanism should also apply for all the LV async apis.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="373055" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/running-a-liveview-test-with-async-true/68765/24">Post #23</a>
	                </div>
	            </div>
              <div id="likers-container-373055" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="373055"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #23"></div>
  </section>
</div>
    <div class="postbit" id="373056" data-post-id="373056">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="LostKobrakai" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/LostKobrakai/120/3072_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  LostKobrakai
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Ah I guess I see the issue now:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  def on_mount(:default, _params, session, socket) do
    if connected?(socket) do
      %{repo: repo, owner: owner} = get_in(session["sandbox"])

      Ecto.Adapters.SQL.Sandbox.allow(repo, owner, self())
    end

    {:cont, socket}
  end
</code></pre>
<p>This is only allowing the current process, but not setting <code>:"$callers"</code> on the process dict.</p>
<p>It would probably need to look like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">def on_mount(:default, _params, session, socket) do
  if connected?(socket) do
    %{repo: repo, owner: owner} = get_in(session["sandbox"])
    Ecto.Adapters.SQL.Sandbox.allow(repo, owner, self())
    Process.put(:"$callers", [owner])
  end

  {:cont, socket}
end
</code></pre>
<p>Edit:<br>
I’ve opened an issue about this:</p>
<p><a href="https://github.com/phoenixframework/phoenix_ecto/issues/195" class="onebox" target="_blank" rel="noopener nofollow">https://github.com/phoenixframework/phoenix_ecto/issues/195</a></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="373056" data-batch-url="/posts/batch_likers">
                        6
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/running-a-liveview-test-with-async-true/68765/25">Post #24</a>
	                </div>
	            </div>
              <div id="likers-container-373056" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="373056"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #24"></div>
  </section>
</div>
    <div class="postbit" id="373064" data-post-id="373064">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="krasenyp" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/krasenyp/120/39733_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  krasenyp
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thank you for taking the time to help us. I hope others too will find the technique and the discussion helpful.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="373064" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/running-a-liveview-test-with-async-true/68765/26">Post #25</a>
	                </div>
	            </div>
              <div id="likers-container-373064" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="373064"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-last-post cat-last-post" title="Last post!"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <span class="all-loaded">— All posts loaded —</span>
</div></template></turbo-stream>