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, citation_mode: :warn,
http_port: 8080, http_port: 8080,
ws_port: 3001, ws_port: 3001,
health_port: 9090 health_port: 9090,
export_concurrency: 8
config :logger, :console, config :logger, :console,
format: "$time $metadata[$level] $message\n", 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), http_port: RuntimeConfig.parse_int(System.get_env("PORT"), 8080),
ws_port: RuntimeConfig.parse_int(System.get_env("WS_PORT"), 3001), ws_port: RuntimeConfig.parse_int(System.get_env("WS_PORT"), 3001),
health_port: RuntimeConfig.parse_int(System.get_env("HEALTH_PORT"), 9090) 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} e -> {:error, e}
end end
@default_max_concurrency 8
@doc """ @doc """
Export all `.org` files found under `notes_dir`. 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 Returns `{:ok, count}` where `count` is the number of successfully
exported files, or `{:error, failures}` if any files failed. 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}") Logger.warning("No .org files found in #{notes_dir}")
{:ok, 0} {:ok, 0}
else 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 = results =
Enum.map(org_files, fn orgfile -> org_files
IO.puts(" exporting: #{orgfile}") |> Task.async_stream(
fn orgfile ->
Logger.info(" exporting: #{orgfile}")
{orgfile, export_file(orgfile, notes_dir, output_dir)} {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 = failures =
Enum.filter(results, fn Enum.filter(results, fn
@@ -109,6 +123,10 @@ defmodule OrgGarden.Export do
end end
end end
defp get_concurrency do
Application.get_env(:org_garden, :export_concurrency, @default_max_concurrency)
end
@doc """ @doc """
Compute the expected `.md` path for a given `.org` file. Compute the expected `.md` path for a given `.org` file.