Add parallel export with configurable concurrency (default: 8)

Use Task.async_stream for parallel org->md export.
Configurable via EXPORT_CONCURRENCY env var or :export_concurrency config.
This commit is contained in:
Ignacio Ballesteros
2026-02-21 21:37:27 +01:00
parent c54c27f2de
commit 87fd311005
4 changed files with 9069 additions and 6 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,8 @@ config :org_garden,
citation_mode: :warn,
http_port: 8080,
ws_port: 3001,
health_port: 9090
health_port: 9090,
export_concurrency: 8
config :logger, :console,
format: "$time $metadata[$level] $message\n",

View File

@@ -40,3 +40,7 @@ config :org_garden,
http_port: RuntimeConfig.parse_int(System.get_env("PORT"), 8080),
ws_port: RuntimeConfig.parse_int(System.get_env("WS_PORT"), 3001),
health_port: RuntimeConfig.parse_int(System.get_env("HEALTH_PORT"), 9090)
# Export parallelism
config :org_garden,
export_concurrency: RuntimeConfig.parse_int(System.get_env("EXPORT_CONCURRENCY"), 8)

View File

@@ -71,9 +71,15 @@ defmodule OrgGarden.Export do
e -> {:error, e}
end
@default_max_concurrency 8
@doc """
Export all `.org` files found under `notes_dir`.
Exports files in parallel for improved performance. The concurrency level
can be configured via the `:export_concurrency` application config or
the `EXPORT_CONCURRENCY` environment variable. Defaults to #{@default_max_concurrency}.
Returns `{:ok, count}` where `count` is the number of successfully
exported files, or `{:error, failures}` if any files failed.
"""
@@ -87,13 +93,21 @@ defmodule OrgGarden.Export do
Logger.warning("No .org files found in #{notes_dir}")
{:ok, 0}
else
Logger.info("Exporting #{length(org_files)} org file(s) from #{notes_dir}")
max_concurrency = get_concurrency()
Logger.info("Exporting #{length(org_files)} org file(s) from #{notes_dir} (concurrency: #{max_concurrency})")
results =
Enum.map(org_files, fn orgfile ->
IO.puts(" exporting: #{orgfile}")
org_files
|> Task.async_stream(
fn orgfile ->
Logger.info(" exporting: #{orgfile}")
{orgfile, export_file(orgfile, notes_dir, output_dir)}
end)
end,
max_concurrency: max_concurrency,
timeout: :infinity,
ordered: false
)
|> Enum.map(fn {:ok, result} -> result end)
failures =
Enum.filter(results, fn
@@ -109,6 +123,10 @@ defmodule OrgGarden.Export do
end
end
defp get_concurrency do
Application.get_env(:org_garden, :export_concurrency, @default_max_concurrency)
end
@doc """
Compute the expected `.md` path for a given `.org` file.