Skip to content

Commit

Permalink
Utils.time_offset is more accurate
Browse files Browse the repository at this point in the history
  • Loading branch information
Neylix committed Dec 22, 2023
1 parent 4b48fc8 commit 0995b46
Show file tree
Hide file tree
Showing 12 changed files with 40 additions and 70 deletions.
6 changes: 2 additions & 4 deletions lib/archethic/beacon_chain/slot_timer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ defmodule Archethic.BeaconChain.SlotTimer do
"""
@spec next_slot(DateTime.t()) :: DateTime.t()
def next_slot(date_from = %DateTime{}) do
get_interval()
|> CronParser.parse!(true)
|> Utils.next_date(date_from)
get_interval() |> Utils.next_date(date_from)
end

@doc """
Expand Down Expand Up @@ -162,7 +160,7 @@ defmodule Archethic.BeaconChain.SlotTimer do
end

defp schedule_new_slot(interval) do
Process.send_after(self(), :new_slot, Utils.time_offset(interval) * 1000)
Process.send_after(self(), :new_slot, Utils.time_offset(interval))
end

def config_change(nil), do: :ok
Expand Down
6 changes: 2 additions & 4 deletions lib/archethic/beacon_chain/summary_timer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ defmodule Archethic.BeaconChain.SummaryTimer do
"""
@spec next_summary(DateTime.t()) :: DateTime.t()
def next_summary(date_from = %DateTime{}) do
get_interval()
|> CronParser.parse!(true)
|> Utils.next_date(date_from)
get_interval() |> Utils.next_date(date_from)
end

@doc """
Expand Down Expand Up @@ -125,7 +123,7 @@ defmodule Archethic.BeaconChain.SummaryTimer do
end

defp schedule_next_summary_time(interval) do
Process.send_after(self(), :next_summary_time, Utils.time_offset(interval) * 1000)
Process.send_after(self(), :next_summary_time, Utils.time_offset(interval))
end

def config_change(nil), do: :ok
Expand Down
5 changes: 2 additions & 3 deletions lib/archethic/contracts/worker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ defmodule Archethic.Contracts.Worker do
alias Archethic.TransactionChain.TransactionData.Recipient
alias Archethic.Utils
alias Archethic.Utils.DetectNodeResponsiveness
alias Crontab.CronExpression.Parser, as: CronParser

@extended_mode? Mix.env() != :prod

Expand Down Expand Up @@ -194,7 +193,7 @@ defmodule Archethic.Contracts.Worker do
defp schedule_trigger(trigger = {:interval, interval}, triggers_type) do
now = DateTime.utc_now()

next_tick = interval |> CronParser.parse!(@extended_mode?) |> Utils.next_date(now)
next_tick = Utils.next_date(interval, now, @extended_mode?)

# do not allow an interval trigger if there is a datetime trigger at same time
# because one of them would get a "transaction is already mining"
Expand All @@ -204,7 +203,7 @@ defmodule Archethic.Contracts.Worker do
"Contract scheduler skips next tick for trigger=interval because there is a trigger=datetime at the same time that takes precedence"
)

interval |> CronParser.parse!(@extended_mode?) |> Utils.next_date(next_tick)
Utils.next_date(interval, next_tick, @extended_mode?)
else
next_tick
end
Expand Down
2 changes: 1 addition & 1 deletion lib/archethic/networking/scheduler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ defmodule Archethic.Networking.Scheduler do
end

defp schedule_update(interval) do
Process.send_after(self(), :update, Utils.time_offset(interval) * 1000)
Process.send_after(self(), :update, Utils.time_offset(interval))
end

defp do_update do
Expand Down
1 change: 0 additions & 1 deletion lib/archethic/oracle_chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ defmodule Archethic.OracleChain do
def next_summary_date(date_from = %DateTime{}) do
Application.get_env(:archethic, Scheduler)
|> Keyword.fetch!(:summary_interval)
|> CronParser.parse!(true)
|> Utils.next_date(date_from)
end

Expand Down
21 changes: 6 additions & 15 deletions lib/archethic/oracle_chain/scheduler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ defmodule Archethic.OracleChain.Scheduler do
alias OracleChain.{Services, Summary}
alias TransactionChain.{Transaction, TransactionData}

alias Crontab.CronExpression.Parser, as: CronParser

use GenStateMachine, callback_mode: [:handle_event_function]
@vsn Mix.Project.config()[:version]

Expand Down Expand Up @@ -69,8 +67,7 @@ defmodule Archethic.OracleChain.Scheduler do
# This case may happen in case of process restart after crash
{:ok, %Node{authorized?: true, available?: true}} ->
summary_date =
Map.get(state_data, :summary_interval)
|> next_date()
Map.get(state_data, :summary_interval) |> Utils.next_date(DateTime.utc_now())

PubSub.register_to_new_transaction_by_type(:oracle)
PubSub.register_to_new_transaction_by_type(:oracle_summary)
Expand Down Expand Up @@ -102,7 +99,7 @@ defmodule Archethic.OracleChain.Scheduler do
_state,
data = %{polling_interval: polling_interval, indexes: indexes, summary_date: summary_date}
) do
polling_date = next_date(polling_interval)
polling_date = Utils.next_date(polling_interval, DateTime.utc_now())

polling_timer =
case Map.get(data, :polling_timer) do
Expand Down Expand Up @@ -419,7 +416,7 @@ defmodule Archethic.OracleChain.Scheduler do
data = %{summary_interval: summary_interval}
) do
# Discard the oracle summary if there is not previous indexing
next_summary_date = next_date(summary_interval)
next_summary_date = Utils.next_date(summary_interval, DateTime.utc_now())
Logger.info("Next Oracle Summary at #{DateTime.to_string(next_summary_date)}")

new_data =
Expand Down Expand Up @@ -469,7 +466,7 @@ defmodule Archethic.OracleChain.Scheduler do
data = %{summary_interval: summary_interval, polling_interval: polling_interval}
) do
if Crypto.first_node_public_key() == first_public_key do
next_summary_date = next_date(summary_interval)
next_summary_date = Utils.next_date(summary_interval, DateTime.utc_now())
index = chain_size(next_summary_date)

other_authorized_nodes =
Expand All @@ -482,7 +479,7 @@ defmodule Archethic.OracleChain.Scheduler do

case other_authorized_nodes do
[] ->
next_polling_date = next_date(polling_interval)
next_polling_date = Utils.next_date(polling_interval, DateTime.utc_now())

new_data =
data
Expand Down Expand Up @@ -591,7 +588,7 @@ defmodule Archethic.OracleChain.Scheduler do
defp update_summary_date(data = %{summary_interval: summary_interval}) do
OracleChain.update_summ_gen_addr()

next_summary_date = next_date(summary_interval)
next_summary_date = Utils.next_date(summary_interval, DateTime.utc_now())
Logger.info("Next Oracle Summary at #{DateTime.to_string(next_summary_date)}")

data
Expand Down Expand Up @@ -738,12 +735,6 @@ defmodule Archethic.OracleChain.Scheduler do
end
end

defp next_date(interval) do
interval
|> CronParser.parse!(true)
|> Utils.next_date(DateTime.utc_now())
end

defp get_new_oracle_data(summary_date, index) do
summary_date
|> Crypto.derive_oracle_address(index)
Expand Down
2 changes: 1 addition & 1 deletion lib/archethic/oracle_chain/services/hydrating_cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ defmodule Archethic.OracleChain.Services.HydratingCache do

defp next_tick_in_seconds(refresh_interval) do
if is_binary(refresh_interval) do
Utils.time_offset(refresh_interval) * 1000
Utils.time_offset(refresh_interval)
else
refresh_interval
end
Expand Down
2 changes: 1 addition & 1 deletion lib/archethic/reward/scheduler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ defmodule Archethic.Reward.Scheduler do
end

defp schedule(interval) do
Process.send_after(self(), :mint_rewards, Utils.time_offset(interval) * 1000)
Process.send_after(self(), :mint_rewards, Utils.time_offset(interval))
end

defp trigger_node?(validation_nodes, count \\ 0) do
Expand Down
2 changes: 0 additions & 2 deletions lib/archethic/self_repair.ex
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,12 @@ defmodule Archethic.SelfRepair do
:archethic
|> Application.get_env(SummaryTimer, [])
|> Keyword.fetch!(:interval)
|> CronParser.parse!(true)
|> Utils.next_date(last_sync_date)

next_repair_date =
:archethic
|> Application.get_env(Scheduler, [])
|> Keyword.fetch!(:interval)
|> CronParser.parse!(true)
|> Utils.next_date(next_summary_date)

DateTime.compare(DateTime.utc_now(), next_repair_date) != :lt
Expand Down
7 changes: 2 additions & 5 deletions lib/archethic/self_repair/scheduler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ defmodule Archethic.SelfRepair.Scheduler do
alias Archethic.Utils

alias Archethic.Bootstrap.Sync, as: BootstrapSync
alias Crontab.CronExpression.Parser, as: CronParser

require Logger

Expand Down Expand Up @@ -169,7 +168,7 @@ defmodule Archethic.SelfRepair.Scheduler do
end

defp schedule_sync(interval) do
Process.send_after(self(), :sync, Utils.time_offset(interval) * 1000)
Process.send_after(self(), :sync, Utils.time_offset(interval))
end

defp last_sync_date_to_string(last_sync_date) do
Expand Down Expand Up @@ -203,8 +202,6 @@ defmodule Archethic.SelfRepair.Scheduler do
"""
@spec next_repair_time(DateTime.t()) :: DateTime.t()
def next_repair_time(ref_time \\ DateTime.utc_now()) do
get_interval()
|> CronParser.parse!(true)
|> Utils.next_date(ref_time)
get_interval() |> Utils.next_date(ref_time)
end
end
11 changes: 2 additions & 9 deletions lib/archethic/shared_secrets/node_renewal_scheduler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ defmodule Archethic.SharedSecrets.NodeRenewalScheduler do
them as new authorized nodes and update the daily nonce seed.
"""

alias Crontab.CronExpression.Parser, as: CronParser

alias Archethic

alias Archethic.Election
Expand Down Expand Up @@ -320,7 +318,7 @@ defmodule Archethic.SharedSecrets.NodeRenewalScheduler do
end

defp schedule_renewal_message(interval) do
Process.send_after(self(), :make_renewal, Utils.time_offset(interval) * 1000)
Process.send_after(self(), :make_renewal, Utils.time_offset(interval))
end

defp trigger_node?(validation_nodes, count \\ 0) do
Expand All @@ -339,14 +337,9 @@ defmodule Archethic.SharedSecrets.NodeRenewalScheduler do
"""
@spec next_application_date(DateTime.t()) :: DateTime.t()
def next_application_date(date_from = %DateTime{}) do
get_application_date_interval()
|> CronParser.parse!(true)
|> Utils.next_date(date_from)
end

defp get_application_date_interval do
Application.get_env(:archethic, __MODULE__)
|> Keyword.fetch!(:application_interval)
|> Utils.next_date(date_from)
end

def code_change(_old_vsn, state, data, _extra), do: {:ok, state, data}
Expand Down
45 changes: 21 additions & 24 deletions lib/archethic/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -60,34 +60,28 @@ defmodule Archethic.Utils do
## Examples
# Time offset for the next 2 seconds
iex> Utils.time_offset("*/2 * * * * *", ~U[2020-09-24 20:13:12.10Z])
2
# 12 seconds + offset == 14 seconds
iex> Utils.time_offset("*/2 * * * * *", ref_time: ~U[2020-09-24 20:13:12.10Z], time_unit: :millisecond)
1900
# Time offset for the next minute
iex> Utils.time_offset("0 * * * * *", ~U[2020-09-24 20:13:12.00Z])
48
# 12 seconds + offset == 60 seconds (1 minute)
iex> Utils.time_offset("0 * * * * *", ref_time: ~U[2020-09-24 20:13:12.00Z])
48000
# Time offset for the next hour
iex> Utils.time_offset("0 0 * * * *", ~U[2020-09-24 20:13:00Z])
iex> Utils.time_offset("0 0 * * * *", ref_time: ~U[2020-09-24 20:13:00Z], time_unit: :second)
2820
# 13 minutes: 720 seconds + offset == 3600 seconds (one hour)
# Time offset for the next day
iex> Utils.time_offset("0 0 0 * * *", ~U[2020-09-24 00:00:01Z])
iex> Utils.time_offset("0 0 0 * * *", ref_time: ~U[2020-09-24 00:00:01Z], time_unit: :second)
86399
# 1 second + offset = 86400 (1 day)
"""
@spec time_offset(cron_interval :: binary()) :: seconds :: non_neg_integer()
def time_offset(interval, ref_time \\ DateTime.utc_now(), extended_mode \\ true) do
next_slot =
interval
|> CronParser.parse!(extended_mode)
|> CronScheduler.get_next_run_date!(DateTime.to_naive(ref_time))
|> DateTime.from_naive!("Etc/UTC")
@spec time_offset(cron_interval :: binary(), opts :: Keyword.t()) :: offset :: non_neg_integer()
def time_offset(interval, opts \\ []) do
ref_time = Keyword.get(opts, :ref_time, DateTime.utc_now())
extended_mode = Keyword.get(opts, :extended_mode, true)
time_unit = Keyword.get(opts, :time_unit, :millisecond)

DateTime.diff(next_slot, ref_time, :second)
interval |> next_date(ref_time, extended_mode) |> DateTime.diff(ref_time, time_unit)
end

@doc """
Expand Down Expand Up @@ -836,20 +830,23 @@ defmodule Archethic.Utils do
## Examples
iex> Utils.next_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:00Z])
iex> Utils.next_date("*/10 * * * * * *", ~U[2022-10-01 01:00:00Z])
~U[2022-10-01 01:00:10Z]
iex> Utils.next_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:00.100Z])
iex> Utils.next_date("*/10 * * * * * *", ~U[2022-10-01 01:00:00.100Z])
~U[2022-10-01 01:00:10Z]
iex> Utils.next_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:05Z])
iex> Utils.next_date("*/10 * * * * * *", ~U[2022-10-01 01:00:05Z])
~U[2022-10-01 01:00:10Z]
iex> Utils.next_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:05.139Z])
iex> Utils.next_date("*/10 * * * * * *", ~U[2022-10-01 01:00:05.139Z])
~U[2022-10-01 01:00:10Z]
"""
@spec next_date(Crontab.CronExpression.t(), DateTime.t()) :: DateTime.t()
def next_date(cron_expression, date_from = %DateTime{}) do
@spec next_date(interval :: binary(), date_from :: DateTime.t(), extended_mode? :: boolean()) ::
next_date :: DateTime.t()
def next_date(interval, date_from = %DateTime{}, extended_mode? \\ true) do
cron_expression = CronParser.parse!(interval, extended_mode?)

naive_date_from = DateTime.to_naive(date_from)

if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do
Expand Down

0 comments on commit 0995b46

Please sign in to comment.