electric icon indicating copy to clipboard operation
electric copied to clipboard

Support shapes on partitioned tables

Open ericallam opened this issue 1 year ago • 0 comments

When trying to use electric on a table that is partitioned:

create table runs (
  id bigint,
  task_id bigint not null,
  payload text,
  status text,
  created_at timestamp with time zone default now(),
  primary key (id, created_at)
)
partition by
  range (created_at);

create table runs_2023 partition of runs for
values
from
  ('2023-01-01') to ('2024-01-01');

create table runs_2024 partition of runs for
values
from
  ('2024-01-01') to ('2025-01-01');

If you try and subscribe to a shape using the run table, the following error occurs:

2024-12-07 21:10:43 21:10:43.713 [info] Query String: offset=-1&table=public.runs
2024-12-07 21:10:43 
2024-12-07 21:10:43 21:10:43.714 [error] GenServer {:"Elixir.Electric.ProcessRegistry:single_stack", {Electric.Postgres.Inspector.EtsInspector, nil}} terminating
2024-12-07 21:10:43 ** (MatchError) no match of right hand side value: []
2024-12-07 21:10:43     (electric 0.9.4) lib/electric/postgres/inspector/direct_inspector.ex:23: Electric.Postgres.Inspector.DirectInspector.load_relation/2
2024-12-07 21:10:43     (electric 0.9.4) lib/electric/postgres/inspector/ets_inspector.ex:114: Electric.Postgres.Inspector.EtsInspector.handle_call/3
2024-12-07 21:10:43     (stdlib 6.0.1) gen_server.erl:2209: :gen_server.try_handle_call/4
2024-12-07 21:10:43     (stdlib 6.0.1) gen_server.erl:2238: :gen_server.handle_msg/6
2024-12-07 21:10:43     (stdlib 6.0.1) proc_lib.erl:329: :proc_lib.init_p_do_apply/3
2024-12-07 21:10:43 Process Label: {:ets_inspector, "single_stack"}
2024-12-07 21:10:43 Last message (from #PID<0.2976.0>): {:load_relation, "public.run"}
2024-12-07 21:10:43 
2024-12-07 21:10:43 21:10:43.717 [error] ** (Plug.Conn.WrapperError) ** (ArgumentError) cannot fetch key "handle" from conn.query_params because they were not fetched. Call Plug.Conn.fetch_query_params/2, either as a plug or directly, to fetch it
2024-12-07 21:10:43     (plug 1.16.1) lib/plug/conn/unfetched.ex:35: Plug.Conn.Unfetched.raise_unfetched/3
2024-12-07 21:10:43     (elixir 1.17.2) lib/access.ex:322: Access.get/3
2024-12-07 21:10:43     (electric 0.9.4) lib/electric/plug/serve_shape_plug.ex:654: Electric.Plug.ServeShapePlug.end_telemetry_span/2
2024-12-07 21:10:43     (electric 0.9.4) lib/electric/plug/serve_shape_plug.ex:689: Electric.Plug.ServeShapePlug.handle_errors/2
2024-12-07 21:10:43     (plug 1.16.1) lib/plug/error_handler.ex:113: Plug.ErrorHandler.__catch__/6
2024-12-07 21:10:43     (electric 0.9.4) deps/plug/lib/plug/router.ex:246: anonymous fn/4 in Electric.Plug.Router.dispatch/2
2024-12-07 21:10:43     (telemetry 1.2.1) /app/deps/telemetry/src/telemetry.erl:321: :telemetry.span/3
2024-12-07 21:10:43     (electric 0.9.4) deps/plug/lib/plug/router.ex:242: Electric.Plug.Router.dispatch/2
2024-12-07 21:10:43     (electric 0.9.4) lib/electric/plug/router.ex:1: Electric.Plug.Router.plug_builder_call/2
2024-12-07 21:10:43     (bandit 1.5.5) lib/bandit/pipeline.ex:124: Bandit.Pipeline.call_plug!/2
2024-12-07 21:10:43     (bandit 1.5.5) lib/bandit/pipeline.ex:36: Bandit.Pipeline.run/4
2024-12-07 21:10:43     (bandit 1.5.5) lib/bandit/http1/handler.ex:12: Bandit.HTTP1.Handler.handle_data/3
2024-12-07 21:10:43     (bandit 1.5.5) lib/bandit/delegating_handler.ex:18: Bandit.DelegatingHandler.handle_data/3
2024-12-07 21:10:43     (bandit 1.5.5) /app/deps/thousand_island/lib/thousand_island/handler.ex:411: Bandit.DelegatingHandler.handle_continue/2
2024-12-07 21:10:43     (stdlib 6.0.1) gen_server.erl:2163: :gen_server.try_handle_continue/3
2024-12-07 21:10:43     (stdlib 6.0.1) gen_server.erl:2072: :gen_server.loop/7
2024-12-07 21:10:43     (stdlib 6.0.1) proc_lib.erl:329: :proc_lib.init_p_do_apply/3

Since queries against the runs table (SELECT * FROM runs) works across partitions, it seems like it should be possible for electric to perform the same "magic".

Currently, electric does work if the partition table name is used instead of the parent table (e.g. runs_2024), but then unfortunately shapes cannot really travel across partitions. This could be a real issue if partitions are small (e.g. every minute).

ericallam avatar Dec 07 '24 21:12 ericallam