8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 1/75
Procesando millones de
imágenesDavid Padilla
@dabit
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 2/75
Carrierwave
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 3/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 4/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 5/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 6/75
image.recreate_versions!
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 7/75
Image.each do |i| i.file.recreate_versions!end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 8/75
2,700,000
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 9/75
1 segundo
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 10/75
2,700,000 ÷ 60
=
45,000 minutos
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 11/75
45,000 ÷ 60
=
750 horas
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 12/75
750 ÷ 24
=
31.25 días
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 13/75
MAX_WORKERS = 20
Image.find_each(batch_size: 1200) do |batch| queue = Queue.new batch.each {|o| queue.push o} workers = (0..MAX_WORKERS).map do | w|
Thread.new do while image = queue.pop(true) image.file.recreate_versions! end end
end
workers.map(&:join)end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 14/75
MAX_WORKERS = 20
Image.find_each(batch_size: 1200) do |batch| queue = Queue.new batch.each {|o| queue.push o} workers = (0..MAX_WORKERS).map do |w|
Thread.new do while image = queue.pop(true) image.file.recreate_versions! end end
end
workers.map(&:join)end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 15/75
MAX_WORKERS = 20
Image.find_each(batch_size: 1200) do |batch| queue = Queue.new batch.each {|o| queue.push o} workers = (0..MAX_WORKERS).map do |w|
Thread.new do while image = queue.pop(true) image.file.recreate_versions! end end
end
workers.map(&:join)end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 16/75
MAX_WORKERS = 20
Image.find_each(batch_size: 1200) do |batch| queue = Queue.new batch.each {|o| queue.push o} workers = (0..MAX_WORKERS).map do |w|
Thread.new do while image = queue.pop(true) image.file.recreate_versions! end end
end
workers.map(&:join)end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 17/75
MAX_WORKERS = 20
Image.find_each(batch_size: 1200) do |batch| queue = Queue.new batch.each {|o| queue.push o} workers = (0..MAX_WORKERS).map do | w|
Thread.new do while image = queue.pop(true) image.file.recreate_versions! end end
end
workers.map(&:join)end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 18/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 19/75
array = []
5.times.map do Thread.new do 1000.times do array << nil end end
end.each(&:join)
puts array.size
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 20/75
$ ruby pushing_nil.rb5000
$ jruby pushing_nil.rb4446
$ rbx pushing_nil.rb3088
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 21/75
array = []
5.times.map do Thread.new do 1000.times do array << nil end end
end.each(&:join)
puts array.size
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 22/75
1 segundo
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 23/75
0.6 segundos
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 24/75
18 días
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 25/75
Elixir
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 26/75
class Code def method(*args) code = code + code
while(true) do something_awesome end end
def something_awesome add = 1 + 2 o = Object.new o.save_to_database endend
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 27/75
Exception
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 28/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 29/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 30/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 31/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 32/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 33/75
Traer registros de la Base de Datos
Bajar de S3 imagen original
Crear dos tamaños
Subir a S3
Crear aplicación
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 34/75
Crear aplicación
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 35/75
$ mix new images
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 36/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 37/75
Traer registros de la Basede Datos
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 38/75
use Mix.Config
config :images, Images.Repo,
adapter: Ecto. Adapters. MySQL, database: "images_database", username: "root", password: "",
hostname: "localhost"
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 39/75
defmodule Images.PropertyImage do use Ecto. Model
schema "images" do field :file, :string end
end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 40/75
defmodule Images.Image do use Ecto. Model
def main_query do
from i in Images.Image, select: i end def find(image_id ) do
from i in main_query
where: id = ^image_id end
def paged(offset, limit) do from i in main_query, limit: ^limit,
offset: ^offset end def all do
Images.Repo.all main_query end
end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 41/75
Bajar de S3 la imagenoriginal
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 42/75
def download_original(filename , id ) do file = temp_filename(filename, id) ibrowse = [save_response_to_file: String.to_char_list(file)] s3_url(filename, id)
|> HTTPotion.get([ibrowse: ibrowse]) fileend
def s3_url(file , id ) do "#{s3_path}/#{id}/#{file}"end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 43/75
def download_original(filename , id ) do file = temp_filename(filename, id) ibrowse = [save_response_to_file: String.to_char_list(file)] s3_url(filename, id)
|> HTTPotion.get([ibrowse: ibrowse]) fileend
def s3_url(file , id ) do "#{s3_path}/#{id}/#{file}"end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 44/75
def download_original(filename , id ) do file = temp_filename(filename, id) ibrowse = [save_response_to_file: String.to_char_list(file)]HTTPotion.get(s3_url(filename, id), [ibrowse: ibrowse])
fileend
def s3_url(file , id ) do "#{s3_path}/#{id}/#{file}"end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 45/75
def download_original(filename , id ) do file = temp_filename(filename, id) ibrowse = [save_response_to_file: String.to_char_list(file)] s3_url(filename, id)
|> HTTPotion.get([ibrowse: ibrowse]) fileend
def s3_url(file , id ) do "#{s3_path}/#{id}/#{file}"end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 46/75
Crear dos tamaños
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 47/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 48/75
def generate_medium (file , filename , id ) do result = Path.join(System .tmp_dir, size_name(filename, :medium ))
Mogrify.open(file) |> Mogrify.copy |> Mogrify.resize_to_fill("450x300") |> Mogrify.save(result)end
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 49/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 50/75
Subir a S3
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 51/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 52/75
{status, _} = System .cmd("s3cmd", ["-P", "put", filename, s3_name])
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 53/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 54/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 55/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 56/75
{_, file} = File.read(result)
Application.get_env(:images, :s3_bucket) |> String.to_char_list |> :erlcloud_s3.put_object(s3_name, file, [], [{#…}])
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 57/75
y la concurrencia?
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 58/75
Traer registros de la Base de Datos
Bajar de S3 imagen original
Crear dos tamaños
Subir a S3
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 59/75
:poolboy
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 60/75
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 61/75
defmodule Images.PropertyImageWorker do use GenServer
def start_link([]) do :gen_server.start_link( __MODULE__ , [], []) end
def init(state ) do secret = to_char_list( Application.get_env(:images, :aws_secret_key))
Application.get_env(:images, :aws_access_key) |> to_char_list |> :erlcloud_s3.configure(secret) {:ok, state} end
def handle_call(image , from , state ) do
result = Images.PropertyImage.process(image) {:reply, [result], state} endend
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 62/75
defmodule Images.PropertyImageWorker do use GenServer
def start_link([]) do :gen_server.start_link(__MODULE__, [], []) end
def init(state ) do secret = to_char_list( Application.get_env(:images, :aws_secret_key))
Application.get_env(:images, :aws_access_key) |> to_char_list |> :erlcloud_s3.configure(secret) {:ok, state} end
def handle_call(image , from , state ) do
result = Images.PropertyImage.process(image) {:reply, [result], state} endend
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 63/75
defmodule Images.ImagesSupervisor do use Supervisor
def start_link do :supervisor.start_link( __MODULE__ , [])
end
def init([]) do poolboy_config = [ {:name, {:local, pool_name()}}, {:worker_module, Images.PropertyImageWorker}, {:size, 20}, {:max_overflow , 0} ]
children = [ :poolboy.child_spec(pool_name(), poolboy_config, []), worker(Images.Repo, []) ]
supervise(children, strategy: :one_for_one) end
def pool_name do :property_images endend
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 64/75
defmodule Images.ImagesSupervisor do use Supervisor
def start_link do :supervisor.start_link(__MODULE__, [])
end
def init([]) do poolboy_config = [ {:name, {:local, pool_name()}}, {:worker_module, Images.PropertyImageWorker}, {:size, 20}, {:max_overflow , 0} ]
children = [ :poolboy.child_spec(pool_name(), poolboy_config, []), worker(Images.Repo, []) ]
supervise(children, strategy: :one_for_one) end
def pool_name do :property_images endend
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 65/75
defmodule Images do def start( _type , _args) do
supervisor = Images.ImagesSupervisor.start_link enqueue supervisor end
def enqueue do Images.PropertyImage.all
|> Enum .each fn(r ) -> spawn(fn() -> pool_image(r) end) end end
def pool_image(image ) do :poolboy.transaction( Images.ImagesSupervisor.pool_name, fn( pid ) -> :gen_server.call(pid, image) end,
:infinity ) endend
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 66/75
defmodule Images do def start( _type , _args) do
supervisor = Images.ImagesSupervisor.start_link enqueue supervisor end
def enqueue do Images.PropertyImage.all
|> Enum .each fn(r ) -> spawn(fn() -> pool_image(r) end) end end
def pool_image(image ) do :poolboy.transaction( Images.ImagesSupervisor.pool_name, fn( pid ) -> :gen_server.call(pid, image) end,
:infinity ) endend
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 67/75
defmodule Images do def start( _type , _args) do
supervisor = Images.ImagesSupervisor.start_link enqueue supervisor end
def enqueue do Images.PropertyImage.all
|> Enum .each fn(r ) -> spawn(fn() -> pool_image(r) end) end end
def pool_image(image ) do :poolboy.transaction( Images.ImagesSupervisor.pool_name, fn( pid ) -> :gen_server.call(pid, image) end,
:infinity ) endend
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 68/75
defmodule Images do def start( _type , _args) do
supervisor = Images.ImagesSupervisor.start_link enqueue supervisor end
def enqueue do Images.PropertyImage.all
|> Enum .each fn(r ) -> spawn(fn() -> pool_image(r) end) end end
def pool_image(image ) do :poolboy.transaction( Images.ImagesSupervisor.pool_name, fn( pid ) -> :gen_server.call(pid, image) end,
:infinity ) endend
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 69/75
BEAM
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 70/75
BEAM
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 71/75
Conclusión
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 72/75
4 días
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 73/75
4 x 24 = 96
96 x 60 = 5,7605,760 x 60 = 345,600
345,600 / 2,700,000 = 0.128s
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 74/75
Conclusión II
8/17/2019 Procesando Millones de Imágenes Ruby vs Elixir
http://slidepdf.com/reader/full/procesando-millones-de-imagenes-ruby-vs-elixir 75/75
Fin