@@ -77,14 +77,16 @@ defmodule NervesHub.Devices do
7777 |> join ( :left , [ d , o , p ] , dp in assoc ( d , :deployment ) )
7878 |> join ( :left , [ d , o , p , dp ] , f in assoc ( dp , :firmware ) )
7979 |> join ( :left , [ d , o , p , dp , f ] , lc in assoc ( d , :latest_connection ) , as: :latest_connection )
80+ |> join ( :left , [ d , o , p , dp , f , lc ] , lh in assoc ( d , :latest_health ) , as: :latest_health )
8081 |> Repo . exclude_deleted ( )
8182 |> sort_devices ( sorting )
8283 |> Filtering . build_filters ( filters )
83- |> preload ( [ d , o , p , dp , f , latest_connection: lc ] ,
84+ |> preload ( [ d , o , p , dp , f , latest_connection: lc , latest_health: lh ] ,
8485 org: o ,
8586 product: p ,
8687 deployment: { dp , firmware: f } ,
87- latest_connection: lc
88+ latest_connection: lc ,
89+ latest_health: lh
8890 )
8991 |> Flop . run ( flop )
9092 end
@@ -120,7 +122,9 @@ defmodule NervesHub.Devices do
120122 |> where ( [ d ] , d . product_id == ^ product . id )
121123 |> Repo . exclude_deleted ( )
122124 |> join ( :left , [ d ] , dc in assoc ( d , :latest_connection ) , as: :latest_connection )
125+ |> join ( :left , [ d , dc ] , dh in assoc ( d , :latest_health ) , as: :latest_health )
123126 |> preload ( [ latest_connection: lc ] , latest_connection: lc )
127+ |> preload ( [ latest_health: lh ] , latest_health: lh )
124128 |> Filtering . build_filters ( filters )
125129 |> sort_devices ( sorting )
126130 |> Flop . run ( flop )
@@ -243,6 +247,12 @@ defmodule NervesHub.Devices do
243247 |> preload ( [ d , o , dp ] , org: o , deployment: dp )
244248 end
245249
250+ defp join_and_preload ( query , assocs ) when is_list ( assocs ) do
251+ Enum . reduce ( assocs , query , fn assoc , q ->
252+ join_and_preload ( q , assoc )
253+ end )
254+ end
255+
246256 defp join_and_preload ( query , nil ) , do: query
247257
248258 defp join_and_preload ( query , :device_certificates ) do
@@ -257,6 +267,12 @@ defmodule NervesHub.Devices do
257267 |> preload ( [ latest_connection: lc ] , latest_connection: lc )
258268 end
259269
270+ defp join_and_preload ( query , :latest_health ) do
271+ query
272+ |> join ( :left , [ d ] , dh in assoc ( d , :latest_health ) , as: :latest_health )
273+ |> preload ( [ latest_health: lh ] , latest_health: lh )
274+ end
275+
260276 def get_device_by_x509 ( cert ) do
261277 fingerprint = NervesHub.Certificate . fingerprint ( cert )
262278
@@ -1145,23 +1161,52 @@ defmodule NervesHub.Devices do
11451161 end
11461162
11471163 def save_device_health ( device_status ) do
1148- device_status
1149- |> DeviceHealth . save ( )
1150- |> Repo . insert ( )
1164+ Multi . new ( )
1165+ |> Multi . insert ( :insert_health , DeviceHealth . save ( device_status ) )
1166+ |> Ecto.Multi . update_all ( :update_device , & update_health_on_device / 1 , [ ] )
1167+ |> Repo . transaction ( )
1168+ |> case do
1169+ { :ok , % { insert_health: health } } ->
1170+ { :ok , health }
1171+
1172+ { :error , _ , changeset , _ } ->
1173+ { :error , changeset }
1174+ end
1175+ end
1176+
1177+ defp update_health_on_device ( % { insert_health: health } ) do
1178+ Device
1179+ |> where ( id: ^ health . device_id )
1180+ |> update ( set: [ latest_health_id: ^ health . id ] )
11511181 end
11521182
11531183 def truncate_device_health ( ) do
1154- days_to_retain =
1184+ interval =
11551185 Application . get_env ( :nerves_hub , :device_health_days_to_retain )
11561186
1157- days_ago = DateTime . shift ( DateTime . utc_now ( ) , day: - days_to_retain )
1187+ delete_limit = Application . get_env ( :nerves_hub , :device_health_delete_limit )
1188+ time_ago = DateTime . shift ( DateTime . utc_now ( ) , day: - interval )
11581189
1159- { count , _ } =
1190+ query =
11601191 DeviceHealth
1161- |> where ( [ dh ] , dh . inserted_at < ^ days_ago )
1162- |> Repo . delete_all ( )
1192+ |> join ( :inner , [ dh ] , d in Device , on: dh . device_id == d . id )
1193+ |> where ( [ dh , _d ] , dh . inserted_at < ^ time_ago )
1194+ |> where ( [ dh , d ] , dh . id != d . latest_health_id )
1195+ |> select ( [ dh ] , dh . id )
1196+ |> limit ( ^ delete_limit )
11631197
1164- { :ok , count }
1198+ { delete_count , _ } =
1199+ DeviceHealth
1200+ |> where ( [ dh ] , dh . id in subquery ( query ) )
1201+ |> Repo . delete_all ( timeout: 30_000 )
1202+
1203+ if delete_count == 0 do
1204+ :ok
1205+ else
1206+ # relax stress on Ecto pool and go again
1207+ Process . sleep ( 2000 )
1208+ truncate_device_health ( )
1209+ end
11651210 end
11661211
11671212 def get_latest_health ( device_id ) do
0 commit comments