Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add abstraction to manage shape request-response behaviour #2273

Merged
merged 25 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d7ff33b
add abstraction to manage shape request-response behaviour
magnetised Jan 29, 2025
0bdf4cb
fix some tests
magnetised Jan 30, 2025
d9ca025
fix tests and tweak api
magnetised Jan 30, 2025
a702ca3
restructure under Shapes.Api
magnetised Jan 30, 2025
66f7f4c
add helper function to configure plug
magnetised Jan 30, 2025
c9d897f
keep original param ordering in location header
magnetised Jan 30, 2025
c0afa95
don't include an offset header if there's no offset
magnetised Jan 30, 2025
b5ff8fc
make delete api more flexible
magnetised Feb 3, 2025
f6e2ce4
fix tests and move config into api directly
magnetised Feb 3, 2025
f4e3039
use plug_opts function
magnetised Feb 3, 2025
3e3fc8c
ignore 404s from delete shape
magnetised Feb 3, 2025
3e0b931
wait for shapes to prevent race conditions
magnetised Feb 3, 2025
2cbd9f5
remove unused function
magnetised Feb 3, 2025
3cefb1f
handle errors when querying initial sync
magnetised Feb 4, 2025
14ca963
improve shape delete handling
magnetised Feb 4, 2025
1d20a72
surface snapshot errors to plug more carefully
magnetised Feb 4, 2025
91625c7
move shape deletion flag into api config
magnetised Feb 4, 2025
73ee59e
move delete and schema repr into api, rename response.shape
magnetised Feb 4, 2025
774e4cc
move deletion config flag test to api
magnetised Feb 4, 2025
c0e4d05
remove nested with clauses
magnetised Feb 5, 2025
b2ea863
[dialyzer]
magnetised Feb 5, 2025
9af4ac6
[dialyzer] remove exunit-related errors from dialyzer
magnetised Feb 5, 2025
569bdf1
Review feedback
magnetised Feb 5, 2025
448d3ac
feedback from review
magnetised Feb 5, 2025
a5aec33
[changeset]
magnetised Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions packages/sync-service/lib/electric/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,19 @@ defmodule Electric.Application do
storage = Electric.Config.get_env(:storage)

router_opts =
[
long_poll_timeout: 20_000,
max_age: Electric.Config.get_env(:cache_max_age),
stale_age: Electric.Config.get_env(:cache_stale_age),
allow_shape_deletion: Electric.Config.get_env(:allow_shape_deletion?)
] ++
Electric.StackSupervisor.build_shared_opts(
stack_id: stack_id,
stack_events_registry: Registry.StackEvents,
storage: storage
)
Electric.Shapes.Api.plug_opts(
[
long_poll_timeout: 20_000,
max_age: Electric.Config.get_env(:cache_max_age),
stale_age: Electric.Config.get_env(:cache_stale_age),
allow_shape_deletion: Electric.Config.get_env(:allow_shape_deletion?)
] ++
Electric.StackSupervisor.build_shared_opts(
stack_id: stack_id,
stack_events_registry: Registry.StackEvents,
storage: storage
)
)

{kv_module, kv_fun, kv_params} =
Electric.Config.get_env(:persistent_kv)
Expand Down
53 changes: 18 additions & 35 deletions packages/sync-service/lib/electric/plug/delete_shape_plug.ex
Original file line number Diff line number Diff line change
@@ -1,61 +1,44 @@
defmodule Electric.Plug.DeleteShapePlug do
require Logger
use Plug.Builder, copy_opts_to_assign: :config

alias Electric.Shapes
alias Electric.Plug.ServeShapePlug.Params
import Electric.Plug.Utils, only: [hold_conn_until_stack_ready: 2]
alias Electric.Shapes.Api

require Logger

plug :fetch_query_params
plug :put_resp_content_type, "application/json"

plug :hold_conn_until_stack_ready

plug :allow_shape_deletion
plug :validate_query_params

plug :validate_request
plug :truncate_or_delete_shape

defp allow_shape_deletion(%Plug.Conn{} = conn, _) do
if conn.assigns.config[:allow_shape_deletion] do
conn
else
conn
|> send_resp(404, Jason.encode_to_iodata!(%{status: "Not found"}))
|> halt()
end
end
defp validate_request(%Plug.Conn{assigns: %{config: config}} = conn, _) do
api = Access.fetch!(config, :api)

defp validate_query_params(%Plug.Conn{} = conn, _) do
all_params =
Map.merge(conn.query_params, conn.path_params)
|> Map.take(["table", "handle"])
|> Map.put("offset", "-1")

case Params.validate(all_params, inspector: conn.assigns.config[:inspector]) do
{:ok, params} ->
%{conn | assigns: Map.merge(conn.assigns, params)}
case Api.validate_for_delete(api, all_params) do
{:ok, request} ->
assign(conn, :request, request)

{:error, error_map} ->
{:error, response} ->
conn
|> send_resp(400, Jason.encode_to_iodata!(error_map))
|> Api.Response.send(response)
|> halt()
end
end

defp truncate_or_delete_shape(%Plug.Conn{} = conn, _) do
if conn.assigns.handle !== nil do
with :ok <- Shapes.clean_shape(conn.assigns.handle, conn.assigns.config) do
send_resp(conn, 202, "")
end
%{assigns: %{request: request}} = conn

if !is_nil(request.handle) do
response = Api.delete_shape(request)

Api.Response.send(conn, response)
else
# FIXME: This has a race condition where we accidentally create a snapshot & shape handle, but clean
# it before snapshot is actually made.
with {shape_handle, _} <-
Shapes.get_or_create_shape_handle(conn.assigns.config, conn.assigns.shape_definition),
:ok <- Shapes.clean_shape(shape_handle, conn.assigns.config) do
send_resp(conn, 202, "")
end
send_resp(conn, 404, Jason.encode!(%{message: "Shape not found"}))
end
end
end
Loading
Loading