Tacnoman

Tacnoman

Hello guys
I must create an api with a great performance and I want to create with Elixir
I have a process (slow) that I must run after some requests. I want to make this flow

In each request, save the data received in memory
After x requests, send to another api (or after x seconds)

In node I can make this:

let batchData = []
const handlerRequest = (req, res) => {
  batchData.push(req. body.data)
  if (batchData > 1000) {
    // Process to send to another api
    batchData = []
  }
  res.json({ success: true })
}

Or

let batchData = []
setInterval(() => {
  if (batchData > 1000) {
    // Process to send to another api
    batchData = []
  }
}, 10000)

const handlerRequest = (req, res) => {
  batchData.push(req. body.data)
  res.json({ success: true })
}

How can I do something like this in Elixir Phoenix?

Thanks for this

Showing Posts 1 to 3

kokolegorille

kokolegorille

You can do this with OTP. Here is a simple GenServer that send itself a regular tick, and check the size of it’s state when data is pushed.

defmodule Demo.Gs do
  use GenServer
  @interval 10_000
  @initial_state <<>>
  
  def start_link(arg \\ @initial_state) do
    GenServer.start_link(__MODULE__, arg, name: __MODULE__)
  end

  def push(data) do
    GenServer.cast(__MODULE__, {:push, data})
  end
  
  def init(arg) do
    Process.send_after(self(), :tick, @interval)
    {:ok, arg}
  end
  
  def handle_cast({:push, data}, state) do
    state = data <> state
    case String.length(state) > 1_000 do
      true -> 
        process(state)
        {:noreply, @initial_state}
      false ->
        {:noreply, state}
    end
  end
  
  def handle_info(:tick, state) do
    process(state)
    Process.send_after(self(), :tick, @interval)
    {:noreply, @initial_state}
  end
  
  defp process(state) do
    IO.puts state
  end
end

It is just a simple example, just printing state after 10 seconds, or when pushed data is bigger than 1_000

PS. You just need to change process() with your own implementation.

Tacnoman

Tacnoman OP

Really, really thanks for this

My final solution:

defmodule Speed2.BatchProcess do
  use GenServer
  @initial_state []
  @interval 10000

  def start_link do
    GenServer.start_link(__MODULE__, [])
  end

  def init(initial_data) do
    Process.send_after(self(), :tick, @interval)
    {:ok, initial_data}
  end

  def handle_info(:tick, state) do
    process(state)
    Process.send_after(self(), :tick, @interval)
    {:noreply, @initial_state}
  end

  def get_my_state(process_id) do
    GenServer.call(process_id, :get_the_state)
  end

  def push(process_id, value) do
    GenServer.call(process_id, {:push, value})
  end

  def clean(process_id) do
    GenServer.call(process_id, :clean)
  end

  def process(state) do
    IO.inspect state
  end

  def handle_call(:get_the_state, _from, my_state) do
    {:reply, my_state, my_state}
  end

  def handle_call({:push, value}, _from, my_state) do
    new_state = my_state ++ [value]
    case Enum.count(new_state) > 10 do
      true -> 
        process(new_state)
        {:reply, new_state, @initial_state}
      false ->
        {:reply, new_state, new_state}
    end
  end

  def handle_call(:clean, _from, my_state) do
    {:reply, my_state, @initial_state}
  end
end
kokolegorille

kokolegorille

Nice, but as an advice I prefer to separate API from Server callbacks. That means putting handle info, init down in your code with server callbacks :slight_smile:

Also…

new_state = my_state ++ [value]

# I would use, which is not the same... 
# but i would push at the head of a list, and maybe reverse later.

new_state = [value | my_state]

# Also

case do end... could also be if do else end...

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
ryanwinchester
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted” Version...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews