fix: updated connections cleanup logic
Some checks are pending
Build / 🚀 Deploy to test env (fly.io) (push) Waiting to run
Build / Manual Approval (push) Blocked by required conditions
Build / 🛠 Build (1.17, 18.x, 27) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/amd64) (push) Blocked by required conditions
Build / 🛠 Build Docker Images (linux/arm64) (push) Blocked by required conditions
Build / merge (push) Blocked by required conditions
Build / 🏷 Create Release (push) Blocked by required conditions

This commit is contained in:
Dmitry Popov
2025-03-13 19:18:29 +01:00
parent 7086413f0c
commit aec0a75e87
7 changed files with 221 additions and 17 deletions

View File

@@ -217,7 +217,6 @@ export enum OutCommand {
getSystemKills = 'get_system_kills',
getSystemsKills = 'get_systems_kills',
openSettings = 'open_settings',
hideActivity = 'hide_activity',
showActivity = 'show_activity',
hideTracking = 'hide_tracking',
showTracking = 'show_tracking',

View File

@@ -32,7 +32,8 @@ defmodule WandererApp.Api.MapState do
default_accept [
:map_id,
:systems_last_activity,
:connections_eol_time
:connections_eol_time,
:connections_start_time
]
defaults [:read, :update, :destroy]
@@ -45,6 +46,7 @@ defmodule WandererApp.Api.MapState do
upsert_fields [
:systems_last_activity,
:connections_eol_time,
:connections_start_time,
:updated_at
]
end
@@ -63,6 +65,10 @@ defmodule WandererApp.Api.MapState do
allow_nil?(true)
end
attribute :connections_start_time, WandererApp.Schema.AshErlangBinary do
allow_nil?(true)
end
attribute :connections_eol_time, WandererApp.Schema.AshErlangBinary do
allow_nil?(true)
end

View File

@@ -81,6 +81,11 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
:timer.hours(get_connection_auto_expire_hours() - get_connection_auto_eol_hours()) +
:timer.minutes(get_eol_expire_timeout_mins())
def get_connection_expire_timeout(),
do:
:timer.hours(get_connection_auto_expire_hours()) +
:timer.minutes(get_eol_expire_timeout_mins())
def init_eol_cache(map_id, connections_eol_time) do
eol_expire_timeout = get_eol_expire_timeout()
@@ -94,6 +99,15 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
end)
end
def init_start_cache(map_id, connections_start_time) when not is_nil(connections_start_time) do
connections_start_time
|> Enum.each(fn {connection_id, start_time} ->
set_start_time(map_id, connection_id, start_time)
end)
end
def init_start_cache(_map_id, _connections_start_time), do: :ok
def add_connection(
%{map_id: map_id} = state,
%{
@@ -158,7 +172,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
),
do:
update_connection(state, :update_time_status, [:time_status], connection_update, fn
%{id: connection_id, time_status: time_status} ->
%{time_status: old_time_status}, %{id: connection_id, time_status: time_status} ->
case time_status == @connection_time_status_eol do
true ->
WandererApp.Cache.put(
@@ -168,7 +182,10 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
)
_ ->
WandererApp.Cache.delete("map_#{map_id}:conn_#{connection_id}:mark_eol_time")
if old_time_status == @connection_time_status_eol do
WandererApp.Cache.delete("map_#{map_id}:conn_#{connection_id}:mark_eol_time")
set_start_time(map_id, connection_id, DateTime.utc_now())
end
end
end)
@@ -205,18 +222,22 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
def cleanup_connections(%{map_id: map_id} = state) do
connection_auto_expire_hours = get_connection_auto_expire_hours()
connection_auto_eol_hours = get_connection_auto_eol_hours()
connection_eol_expire_timeout_hours = get_eol_expire_timeout_mins() / 60
state =
map_id
|> WandererApp.Map.list_connections!()
|> Enum.filter(fn %{
id: connection_id,
inserted_at: inserted_at,
solar_system_source: solar_system_source_id,
solar_system_target: solar_system_target_id,
type: type
} ->
connection_start_time = get_start_time(map_id, connection_id)
type != @connection_type_stargate &&
DateTime.diff(DateTime.utc_now(), inserted_at, :hour) >=
DateTime.diff(DateTime.utc_now(), connection_start_time, :hour) >=
connection_auto_eol_hours &&
is_connection_valid(
:wormholes,
@@ -242,7 +263,6 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
|> WandererApp.Map.list_connections!()
|> Enum.filter(fn %{
id: connection_id,
inserted_at: inserted_at,
solar_system_source: solar_system_source_id,
solar_system_target: solar_system_target_id,
type: type
@@ -273,10 +293,9 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
not is_connection_exist ||
(type != @connection_type_stargate && is_connection_valid &&
(DateTime.diff(DateTime.utc_now(), inserted_at, :hour) >=
connection_auto_expire_hours ||
DateTime.diff(DateTime.utc_now(), connection_mark_eol_time, :hour) >=
connection_auto_expire_hours - connection_auto_eol_hours))
DateTime.diff(DateTime.utc_now(), connection_mark_eol_time, :hour) >=
connection_auto_expire_hours - connection_auto_eol_hours +
+connection_eol_expire_timeout_hours)
end)
|> Enum.reduce(state, fn %{
solar_system_source: solar_system_source_id,
@@ -341,6 +360,10 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
type: connection_type
})
if connection_type == @connection_type_wormhole do
set_start_time(map_id, connection.id, DateTime.utc_now())
end
WandererApp.Map.add_connection(map_id, connection)
Impl.broadcast!(map_id, :maybe_select_system, %{
@@ -386,6 +409,24 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
end
end
def get_start_time(map_id, connection_id) do
case WandererApp.Cache.get("map_#{map_id}:conn_#{connection_id}:start_time") do
nil ->
set_start_time(map_id, connection_id, DateTime.utc_now())
DateTime.utc_now()
value ->
value
end
end
def set_start_time(map_id, connection_id, start_time) do
WandererApp.Cache.put(
"map_#{map_id}:conn_#{connection_id}:start_time",
start_time
)
end
def maybe_add_connection(_map_id, _location, _old_location, _character_id), do: :ok
def can_add_location(_scope, nil), do: false
@@ -499,6 +540,8 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
Impl.broadcast!(map_id, :remove_connections, [connection])
map_id |> WandererApp.Map.remove_connection(connection)
WandererApp.Cache.delete("map_#{map_id}:conn_#{connection.id}:start_time")
_error ->
:ok
end
@@ -534,7 +577,7 @@ defmodule WandererApp.Map.Server.ConnectionsImpl do
connection |> Map.merge(update_map)
) do
if not is_nil(callback_fn) do
callback_fn.(updated_connection)
callback_fn.(connection, updated_connection)
end
Impl.broadcast!(map_id, :update_connection, updated_connection)

View File

@@ -90,7 +90,7 @@ defmodule WandererApp.Map.Server.Impl do
Process.send_after(self(), :update_characters, @update_characters_timeout)
Process.send_after(self(), :update_tracked_characters, 100)
Process.send_after(self(), :update_presence, @update_presence_timeout)
Process.send_after(self(), :cleanup_connections, 5000)
Process.send_after(self(), :cleanup_connections, 5_000)
Process.send_after(self(), :cleanup_systems, 10_000)
Process.send_after(self(), :cleanup_characters, :timer.minutes(5))
Process.send_after(self(), :backup_state, @backup_state_timeout)
@@ -359,9 +359,12 @@ defmodule WandererApp.Map.Server.Impl do
end
end)
connections_eol_time =
connections =
map_id
|> WandererApp.Map.list_connections!()
connections_eol_time =
connections
|> Enum.reduce(%{}, fn %{id: connection_id} = _connection, acc ->
case WandererApp.Cache.get("map_#{map_id}:conn_#{connection_id}:mark_eol_time") do
nil ->
@@ -372,10 +375,18 @@ defmodule WandererApp.Map.Server.Impl do
end
end)
connections_start_time =
connections
|> Enum.reduce(%{}, fn %{id: connection_id} = _connection, acc ->
connection_start_time = ConnectionsImpl.get_start_time(map_id, connection_id)
acc |> Map.put_new(connection_id, connection_start_time)
end)
WandererApp.Api.MapState.create(%{
map_id: map_id,
systems_last_activity: systems_last_activity,
connections_eol_time: connections_eol_time
connections_eol_time: connections_eol_time,
connections_start_time: connections_start_time
})
end
@@ -396,10 +407,12 @@ defmodule WandererApp.Map.Server.Impl do
{:ok,
%{
systems_last_activity: systems_last_activity,
connections_eol_time: connections_eol_time
connections_eol_time: connections_eol_time,
connections_start_time: connections_start_time
}} ->
SystemsImpl.init_last_activity_cache(map_id, systems_last_activity)
ConnectionsImpl.init_eol_cache(map_id, connections_eol_time)
ConnectionsImpl.init_start_cache(map_id, connections_start_time)
state

View File

@@ -82,8 +82,7 @@ defmodule WandererAppWeb.MapEventHandler do
]
@map_activity_ui_events [
"show_activity",
"hide_activity"
"show_activity"
]
@map_routes_events [

View File

@@ -0,0 +1,21 @@
defmodule WandererApp.Repo.Migrations.MapStateConnectionsStartTime do
@moduledoc """
Updates resources based on their most recent snapshots.
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""
use Ecto.Migration
def up do
alter table(:map_state_v1) do
add :connections_start_time, :binary
end
end
def down do
alter table(:map_state_v1) do
remove :connections_start_time
end
end
end

View File

@@ -0,0 +1,123 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"gen_random_uuid()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "systems_last_activity",
"type": "binary"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "connections_start_time",
"type": "binary"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "connections_eol_time",
"type": "binary"
},
{
"allow_nil?": false,
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "inserted_at",
"type": "utc_datetime_usec"
},
{
"allow_nil?": false,
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "updated_at",
"type": "utc_datetime_usec"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"deferrable": false,
"destination_attribute": "id",
"destination_attribute_default": null,
"destination_attribute_generated": null,
"index?": false,
"match_type": null,
"match_with": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "map_state_v1_map_id_fkey",
"on_delete": null,
"on_update": null,
"primary_key?": true,
"schema": "public",
"table": "maps_v1"
},
"size": null,
"source": "map_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"custom_statements": [],
"has_create_action": true,
"hash": "D554156984BC94421114631306D835FDF58CA4A3FFE70CCD436B9DA006F9A96E",
"identities": [
{
"all_tenants?": false,
"base_filter": null,
"index_name": "map_state_v1_uniq_map_id_index",
"keys": [
{
"type": "atom",
"value": "map_id"
}
],
"name": "uniq_map_id",
"nils_distinct?": true,
"where": null
}
],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.WandererApp.Repo",
"schema": null,
"table": "map_state_v1"
}