-
Notifications
You must be signed in to change notification settings - Fork 0
/
threading_and_code_execution.html
422 lines (395 loc) · 37.3 KB
/
threading_and_code_execution.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
<!doctype html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Subprocesos y Ejecución de Código en Rails — Ruby on Rails Guides</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style-v2.css" data-turbo-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/print-v2.css" media="print">
<link rel="stylesheet" type="text/css" href="stylesheets/highlight-v2.css" data-turbo-track="reload">
<link rel="icon" href="images/favicon.ico" sizes="any">
<link rel="apple-touch-icon" href="images/icon.png">
<script src="javascripts/@hotwired--turbo.js" data-turbo-track="reload"></script>
<script src="javascripts/clipboard.js" data-turbo-track="reload"></script>
<script src="javascripts/guides.js" data-turbo-track="reload"></script>
<meta property="og:title" content="Subprocesos y Ejecución de Código en Rails — Ruby on Rails Guides" />
<meta name="description" content="Subprocesos y Ejecución de Código en RailsDespués de leer esta guía, sabrás: Qué código ejecutará Rails automáticamente de manera concurrente Cómo integrar la concurrencia manual con los internos de Rails Cómo envolver todo el código de la aplicación Cómo afectar la recarga de la aplicación" />
<meta property="og:description" content="Subprocesos y Ejecución de Código en RailsDespués de leer esta guía, sabrás: Qué código ejecutará Rails automáticamente de manera concurrente Cómo integrar la concurrencia manual con los internos de Rails Cómo envolver todo el código de la aplicación Cómo afectar la recarga de la aplicación" />
<meta property="og:locale" content="en_US" />
<meta property="og:site_name" content="Ruby on Rails Guides" />
<meta property="og:image" content="https://avatars.githubusercontent.com/u/4223" />
<meta property="og:type" content="website" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Arabic:[email protected]&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Heebo:[email protected]&family=Noto+Sans+Arabic:[email protected]&display=swap" rel="stylesheet">
<meta name="theme-color" content="#C81418">
</head>
<body class="guide">
<nav id="topNav" aria-label="Secondary">
<div class="wrapper">
<strong class="more-info-label">Más en <a href="https://rubyonrails.org/">rubyonrails.org:</a> </strong>
<span class="red-button more-info-button">
Más Ruby on Rails
</span>
<ul class="more-info-links s-hidden">
<li class="more-info"><a href="https://rubyonrails.org/blog">Blog</a></li>
<li class="more-info"><a href="https://guides.rubyonrails.org/">Guías</a></li>
<li class="more-info"><a href="https://api.rubyonrails.org/">API</a></li>
<li class="more-info"><a href="https://discuss.rubyonrails.org/">Foro</a></li>
<li class="more-info"><a href="https://github.com/rails/rails">Contribuir en GitHub</a></li>
</ul>
</div>
</nav>
<header id="page_header">
<div class="wrapper clearfix">
<nav id="feature_nav">
<div class="header-logo">
<a href="index.html" title="Regresar a la página principal de Guías para Edge">Guías</a>
<span id="version_switcher">
Versión:
<select class="guides-version">
<option value="https://edgeguides.rubyonrails.org/" selected>Edge</option>
<option value="https://guides.rubyonrails.org/v7.2/">7.2</option>
<option value="https://guides.rubyonrails.org/v7.1/">7.1</option>
<option value="https://guides.rubyonrails.org/v7.0/">7.0</option>
<option value="https://guides.rubyonrails.org/v6.1/">6.1</option>
<option value="https://guides.rubyonrails.org/v6.0/">6.0</option>
<option value="https://guides.rubyonrails.org/v5.2/">5.2</option>
<option value="https://guides.rubyonrails.org/v5.1/">5.1</option>
<option value="https://guides.rubyonrails.org/v5.0/">5.0</option>
<option value="https://guides.rubyonrails.org/v4.2/">4.2</option>
<option value="https://guides.rubyonrails.org/v4.1/">4.1</option>
<option value="https://guides.rubyonrails.org/v4.0/">4.0</option>
<option value="https://guides.rubyonrails.org/v3.2/">3.2</option>
<option value="https://guides.rubyonrails.org/v3.1/">3.1</option>
<option value="https://guides.rubyonrails.org/v3.0/">3.0</option>
<option value="https://guides.rubyonrails.org/v2.3/">2.3</option>
</select>
</span>
</div>
<ul class="nav">
<li><a class="nav-item" id="home_nav" href="https://rubyonrails.org/">Inicio</a></li>
<li class="guides-index guides-index-large">
<a href="index.html" id="guidesMenu" class="guides-index-item nav-item">Índice de Guías</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<dl class="guides-section-container">
<div class="guides-section">
<dt>Comienza Aquí</dt>
<dd><a href="getting_started.html">Primeros Pasos con Rails</a></dd>
</div>
<div class="guides-section">
<dt>Modelos</dt>
<dd><a href="active_record_basics.html">Conceptos Básicos de Active Record</a></dd>
<dd><a href="active_record_migrations.html">Migraciones de Active Record</a></dd>
<dd><a href="active_record_validations.html">Validaciones de Active Record</a></dd>
</div>
<div class="guides-section">
<dt>Vistas</dt>
<dd><a href="action_view_overview.html">Resumen de Action View</a></dd>
<dd><a href="layouts_and_rendering.html">Diseños y Renderizado en Rails</a></dd>
</div>
<div class="guides-section">
<dt>Controladores</dt>
<dd><a href="action_controller_overview.html">Resumen de Action Controller</a></dd>
<dd><a href="routing.html">Enrutamiento en Rails desde el Exterior</a></dd>
</div>
<div class="guides-section">
<dt>Otros Componentes</dt>
<dd><a href="active_support_core_extensions.html">Extensiones Básicas de Active Support</a></dd>
<dd><a href="action_mailer_basics.html">Conceptos Básicos de Action Mailer</a></dd>
<dd><a href="action_mailbox_basics.html">Conceptos Básicos de Action Mailbox</a></dd>
<dd><a href="action_text_overview.html">Resumen de Action Text</a></dd>
<dd><a href="active_job_basics.html">Conceptos Básicos de Active Job</a></dd>
</div>
<div class="guides-section">
<dt>Políticas</dt>
<dd><a href="maintenance_policy.html">Política de Mantenimiento</a></dd>
</div>
<div class="guides-section">
<dt>Notas de Lanzamiento</dt>
<dd><a href="upgrading_ruby_on_rails.html">Actualizando Ruby on Rails</a></dd>
<dd><a href="7_2_release_notes.html">Versión 7.2 - ?</a></dd>
<dd><a href="7_1_release_notes.html">Versión 7.1 - Octubre 2023</a></dd>
<dd><a href="7_0_release_notes.html">Versión 7.0 - Diciembre 2021</a></dd>
<dd><a href="6_1_release_notes.html">Versión 6.1 - Diciembre 2020</a></dd>
</div>
</dl>
</div>
</li>
<li><a class="nav-item" href="contributing_to_ruby_on_rails.html">Contribuir</a></li>
<li class="guides-index guides-index-small">
<select class="guides-index-item nav-item">
<option value="index.html">Índice de Guías</option>
<optgroup label="Comienza Aquí">
<option value="getting_started.html">Primeros Pasos con Rails</option>
</optgroup>
<optgroup label="Modelos">
<option value="active_record_basics.html">Conceptos Básicos de Active Record</option>
<option value="active_record_migrations.html">Migraciones de Active Record</option>
<option value="active_record_validations.html">Validaciones de Active Record</option>
</optgroup>
<optgroup label="Vistas">
<option value="action_view_overview.html">Resumen de Action View</option>
<option value="layouts_and_rendering.html">Diseños y Renderizado en Rails</option>
</optgroup>
<optgroup label="Controladores">
<option value="action_controller_overview.html">Resumen de Action Controller</option>
<option value="routing.html">Enrutamiento en Rails desde el Exterior</option>
</optgroup>
<optgroup label="Otros Componentes">
<option value="active_support_core_extensions.html">Extensiones Básicas de Active Support</option>
<option value="action_mailer_basics.html">Conceptos Básicos de Action Mailer</option>
<option value="action_mailbox_basics.html">Conceptos Básicos de Action Mailbox</option>
<option value="action_text_overview.html">Resumen de Action Text</option>
<option value="active_job_basics.html">Conceptos Básicos de Active Job</option>
</optgroup>
<optgroup label="Políticas">
<option value="maintenance_policy.html">Política de Mantenimiento</option>
</optgroup>
<optgroup label="Notas de Lanzamiento">
<option value="upgrading_ruby_on_rails.html">Actualizando Ruby on Rails</option>
<option value="7_2_release_notes.html">Versión 7.2 - ?</option>
<option value="7_1_release_notes.html">Versión 7.1 - Octubre 2023</option>
<option value="7_0_release_notes.html">Versión 7.0 - Diciembre 2021</option>
<option value="6_1_release_notes.html">Versión 6.1 - Diciembre 2020</option>
</optgroup>
</select>
</li>
</ul>
</nav>
</div>
</header>
<hr class="hide" />
<section id="feature">
<div class="wrapper">
<h1>Subprocesos y Ejecución de Código en Rails</h1><p>Después de leer esta guía, sabrás:</p>
<ul>
<li>Qué código ejecutará Rails automáticamente de manera concurrente</li>
<li>Cómo integrar la concurrencia manual con los internos de Rails</li>
<li>Cómo envolver todo el código de la aplicación</li>
<li>Cómo afectar la recarga de la aplicación</li>
</ul>
<nav id="subCol">
<h3 class="chapter">
<picture>
<!-- Using the `source` HTML tag to set the dark theme image -->
<source
srcset="images/icon_book-close-bookmark-1-wht.svg"
media="(prefers-color-scheme: dark)"
/>
<img src="images/icon_book-close-bookmark-1.svg" alt="Chapter Icon" />
</picture>
Chapters
</h3>
<ol class="chapters">
<li><a href="#concurrencia-automática">Concurrencia Automática</a></li>
<li><a href="#ejecutor">Ejecutor</a>
<ul>
<li><a href="#devoluciones-de-llamada-por-defecto">Devoluciones de Llamada por Defecto</a></li>
<li><a href="#envolviendo-el-código-de-la-aplicación">Envolviendo el Código de la Aplicación</a></li>
<li><a href="#ejecutor-concurrencia">Concurrencia</a></li>
</ul></li>
<li><a href="#recargador">Recargador</a>
<ul>
<li><a href="#devoluciones-de-llamada">Devoluciones de Llamada</a></li>
<li><a href="#descarga-de-clases">Descarga de Clases</a></li>
<li><a href="#recargador-concurrencia">Concurrencia</a></li>
</ul></li>
<li><a href="#comportamiento-del-marco">Comportamiento del Marco</a>
<ul>
<li><a href="#configuración">Configuración</a></li>
</ul></li>
<li><a href="#bloqueo-de-carga">Bloqueo de Carga</a>
<ul>
<li><a href="#permit-concurrent-loads"><code>permit_concurrent_loads</code></a></li>
<li><a href="#actiondispatch-debuglocks">ActionDispatch::DebugLocks</a></li>
</ul></li>
</ol>
</nav>
<hr>
</div>
</section>
<main id="container">
<div class="wrapper">
<div id="mainCol">
<h2 id="concurrencia-automática"><a class="anchorlink" href="#concurrencia-automática"><span>1</span> Concurrencia Automática</a></h2><p>Rails permite automáticamente que varias operaciones se realicen al mismo tiempo.</p><p>Al usar un servidor web con subprocesos, como el Puma por defecto, se atenderán múltiples solicitudes HTTP simultáneamente, proporcionando a cada solicitud su propia instancia de controlador.</p><p>Los adaptadores de Active Job con subprocesos, incluidos los Async integrados, ejecutarán de manera similar varios trabajos al mismo tiempo. Los canales de Action Cable se gestionan de esta manera también.</p><p>Estos mecanismos implican múltiples subprocesos, cada uno gestionando el trabajo para una instancia única de algún objeto (controlador, trabajo, canal), mientras comparten el espacio de proceso global (como clases y sus configuraciones, y variables globales). Siempre que tu código no modifique ninguna de esas cosas compartidas, puede ignorar principalmente que existen otros subprocesos.</p><p>El resto de esta guía describe los mecanismos que Rails utiliza para hacerlos "principalmente ignorables", y cómo las extensiones y aplicaciones con necesidades especiales pueden usarlos.</p><h2 id="ejecutor"><a class="anchorlink" href="#ejecutor"><span>2</span> Ejecutor</a></h2><p>El Ejecutor de Rails separa el código de la aplicación del código del marco: cada vez que el marco invoca el código que has escrito en tu aplicación, estará envuelto por el Ejecutor.</p><p>El Ejecutor consta de dos devoluciones de llamada: <code>to_run</code> y <code>to_complete</code>. La devolución de llamada Run se llama antes del código de la aplicación, y la devolución de llamada Complete se llama después.</p><h3 id="devoluciones-de-llamada-por-defecto"><a class="anchorlink" href="#devoluciones-de-llamada-por-defecto"><span>2.1</span> Devoluciones de Llamada por Defecto</a></h3><p>En una aplicación Rails por defecto, las devoluciones de llamada del Ejecutor se utilizan para:</p>
<ul>
<li>rastrear qué subprocesos están en posiciones seguras para la carga automática y recarga</li>
<li>habilitar y deshabilitar la caché de consultas de Active Record</li>
<li>devolver las conexiones de Active Record adquiridas al grupo</li>
<li>restringir la duración de vida de la caché interna</li>
</ul>
<p>Antes de Rails 5.0, algunos de estos se manejaban mediante clases de middleware de Rack separadas (como <code>ActiveRecord::ConnectionAdapters::ConnectionManagement</code>), o envolviendo directamente el código con métodos como <code>ActiveRecord::Base.connection_pool.with_connection</code>. El Ejecutor reemplaza estos con una interfaz más abstracta.</p><h3 id="envolviendo-el-código-de-la-aplicación"><a class="anchorlink" href="#envolviendo-el-código-de-la-aplicación"><span>2.2</span> Envolviendo el Código de la Aplicación</a></h3><p>Si estás escribiendo una biblioteca o componente que invocará el código de la aplicación, deberías envolverlo con una llamada al ejecutor:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="c1"># llama al código de la aplicación aquí</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="Rails.application.executor.wrap do
# llama al código de la aplicación aquí
end
">Copy</button>
</div>
<p>CONSEJO: Si invocas repetidamente el código de la aplicación desde un proceso de larga duración, es posible que desees envolver usando el <a href="#recargador">Recargador</a> en su lugar.</p><p>Cada subproceso debe estar envuelto antes de ejecutar el código de la aplicación, por lo que si tu aplicación delega manualmente el trabajo a otros subprocesos, como a través de <code>Thread.new</code> o características de Concurrent Ruby que usan grupos de subprocesos, debes envolver inmediatamente el bloque:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="no">Thread</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="c1"># tu código aquí</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="Thread.new do
Rails.application.executor.wrap do
# tu código aquí
end
end
">Copy</button>
</div>
<p>NOTA: Concurrent Ruby usa un <code>ThreadPoolExecutor</code>, que a veces configura con una opción <code>executor</code>. A pesar del nombre, no está relacionado.</p><p>El Ejecutor es seguro y reentrante; si ya está activo en el subproceso actual, <code>wrap</code> es una operación nula.</p><p>Si no es práctico envolver el código de la aplicación en un bloque (por ejemplo, la API de Rack hace esto problemático), también puedes usar el par <code>run!</code> / <code>complete!</code>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="no">Thread</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
<span class="n">execution_context</span> <span class="o">=</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">run!</span>
<span class="c1"># tu código aquí</span>
<span class="k">ensure</span>
<span class="n">execution_context</span><span class="p">.</span><span class="nf">complete!</span> <span class="k">if</span> <span class="n">execution_context</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="Thread.new do
execution_context = Rails.application.executor.run!
# tu código aquí
ensure
execution_context.complete! if execution_context
end
">Copy</button>
</div>
<h3 id="ejecutor-concurrencia"><a class="anchorlink" href="#ejecutor-concurrencia"><span>2.3</span> Concurrencia</a></h3><p>El Ejecutor pondrá el subproceso actual en modo <code>running</code> en el <a href="#bloqueo-de-carga">Bloqueo de Carga</a>. Esta operación se bloqueará temporalmente si otro subproceso está actualmente ya sea cargando automáticamente una constante o descargando/recargando la aplicación.</p><h2 id="recargador"><a class="anchorlink" href="#recargador"><span>3</span> Recargador</a></h2><p>Al igual que el Ejecutor, el Recargador también envuelve el código de la aplicación. Si el Ejecutor no está ya activo en el subproceso actual, el Recargador lo invocará por ti, por lo que solo necesitas llamar a uno. Esto también garantiza que todo lo que hace el Recargador, incluidas todas sus invocaciones de devolución de llamada, ocurra envuelto dentro del Ejecutor.</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">reloader</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="c1"># llama al código de la aplicación aquí</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="Rails.application.reloader.wrap do
# llama al código de la aplicación aquí
end
">Copy</button>
</div>
<p>El Recargador solo es adecuado donde un proceso de nivel de marco de larga duración llama repetidamente al código de la aplicación, como para un servidor web o una cola de trabajos. Rails envuelve automáticamente las solicitudes web y los trabajadores de Active Job, por lo que rara vez necesitarás invocar el Recargador por ti mismo. Siempre considera si el Ejecutor es una mejor opción para tu caso de uso.</p><h3 id="devoluciones-de-llamada"><a class="anchorlink" href="#devoluciones-de-llamada"><span>3.1</span> Devoluciones de Llamada</a></h3><p>Antes de entrar en el bloque envuelto, el Recargador verificará si la aplicación en ejecución necesita ser recargada, por ejemplo, porque se ha modificado el archivo fuente de un modelo. Si determina que se requiere una recarga, esperará hasta que sea seguro, y luego lo hará, antes de continuar. Cuando la aplicación está configurada para recargar siempre independientemente de si se detectan cambios, la recarga se realiza al final del bloque.</p><p>El Recargador también proporciona devoluciones de llamada <code>to_run</code> y <code>to_complete</code>; se invocan en los mismos puntos que los del Ejecutor, pero solo cuando la ejecución actual ha iniciado una recarga de la aplicación. Cuando no se considera necesaria una recarga, el Recargador invocará el bloque envuelto sin otras devoluciones de llamada.</p><h3 id="descarga-de-clases"><a class="anchorlink" href="#descarga-de-clases"><span>3.2</span> Descarga de Clases</a></h3><p>La parte más significativa del proceso de recarga es la Descarga de Clases, donde se eliminan todas las clases cargadas automáticamente, listas para ser cargadas de nuevo. Esto ocurrirá inmediatamente antes de la devolución de llamada Run o Complete, dependiendo de la configuración <code>reload_classes_only_on_change</code>.</p><p>A menudo, se necesitan realizar acciones de recarga adicionales ya sea justo antes o justo después de la Descarga de Clases, por lo que el Recargador también proporciona devoluciones de llamada <code>before_class_unload</code> y <code>after_class_unload</code>.</p><h3 id="recargador-concurrencia"><a class="anchorlink" href="#recargador-concurrencia"><span>3.3</span> Concurrencia</a></h3><p>Solo los procesos de "alto nivel" de larga duración deben invocar el Recargador, porque si determina que se necesita una recarga, se bloqueará hasta que todos los demás subprocesos hayan completado cualquier invocación del Ejecutor.</p><p>Si esto ocurriera en un subproceso "hijo", con un padre en espera dentro del Ejecutor, causaría un bloqueo inevitable: la recarga debe ocurrir antes de que se ejecute el subproceso hijo, pero no puede realizarse de manera segura mientras el subproceso padre está en medio de la ejecución. Los subprocesos hijos deben usar el Ejecutor en su lugar.</p><h2 id="comportamiento-del-marco"><a class="anchorlink" href="#comportamiento-del-marco"><span>4</span> Comportamiento del Marco</a></h2><p>Los componentes del marco de Rails también usan estas herramientas para gestionar sus propias necesidades de concurrencia.</p><p><code>ActionDispatch::Executor</code> y <code>ActionDispatch::Reloader</code> son middlewares de Rack que envuelven solicitudes con un Ejecutor o Recargador suministrado, respectivamente. Se incluyen automáticamente en la pila de aplicaciones por defecto. El Recargador garantizará que cualquier solicitud HTTP entrante se sirva con una copia recién cargada de la aplicación si han ocurrido cambios en el código.</p><p>Active Job también envuelve sus ejecuciones de trabajo con el Recargador, cargando el último código para ejecutar cada trabajo a medida que sale de la cola.</p><p>Action Cable usa el Ejecutor en su lugar: debido a que una conexión de Cable está vinculada a una instancia específica de una clase, no es posible recargar para cada mensaje de WebSocket entrante. Sin embargo, solo el controlador de mensajes está envuelto; una conexión de Cable de larga duración no impide una recarga que se active por una nueva solicitud entrante o trabajo. En su lugar, Action Cable usa la devolución de llamada <code>before_class_unload</code> del Recargador para desconectar todas sus conexiones. Cuando el cliente se reconecta automáticamente, estará hablando con la nueva versión del código.</p><p>Los anteriores son los puntos de entrada al marco, por lo que son responsables de garantizar que sus respectivos subprocesos estén protegidos y de decidir si es necesaria una recarga. Otros componentes solo necesitan usar el Ejecutor cuando generan subprocesos adicionales.</p><h3 id="configuración"><a class="anchorlink" href="#configuración"><span>4.1</span> Configuración</a></h3><p>El Recargador solo verifica los cambios de archivo cuando <code>config.enable_reloading</code> es <code>true</code> y también lo es <code>config.reload_classes_only_on_change</code>. Estos son los valores predeterminados en el entorno <code>development</code>.</p><p>Cuando <code>config.enable_reloading</code> es <code>false</code> (en <code>production</code>, por defecto), el Recargador es solo un paso hacia el Ejecutor.</p><p>El Ejecutor siempre tiene un trabajo importante que hacer, como la gestión de conexiones de base de datos. Cuando <code>config.enable_reloading</code> es <code>false</code> y <code>config.eager_load</code> es <code>true</code> (valores predeterminados de <code>production</code>), no se producirá recarga, por lo que no necesita el Bloqueo de Carga. Con la configuración predeterminada en el entorno <code>development</code>, el Ejecutor usará el Bloqueo de Carga para garantizar que las constantes solo se carguen cuando sea seguro.</p><h2 id="bloqueo-de-carga"><a class="anchorlink" href="#bloqueo-de-carga"><span>5</span> Bloqueo de Carga</a></h2><p>El Bloqueo de Carga permite que la carga automática y la recarga estén habilitadas en un entorno de tiempo de ejecución multihilo.</p><p>Cuando un subproceso está realizando una carga automática evaluando la definición de clase desde el archivo apropiado, es importante que ningún otro subproceso encuentre una referencia a la constante parcialmente definida.</p><p>De manera similar, solo es seguro realizar una descarga/recarga cuando no se está ejecutando código de aplicación: después de la recarga, la constante <code>User</code>, por ejemplo, puede apuntar a una clase diferente. Sin esta regla, una recarga mal sincronizada significaría que <code>User.new.class == User</code>, o incluso <code>User == User</code>, podría ser falso.</p><p>Ambas restricciones son abordadas por el Bloqueo de Carga. Lleva un registro de qué subprocesos están ejecutando actualmente código de aplicación, cargando una clase o descargando constantes cargadas automáticamente.</p><p>Solo un subproceso puede cargar o descargar a la vez, y para hacer cualquiera de los dos, debe esperar hasta que ningún otro subproceso esté ejecutando código de aplicación. Si un subproceso está esperando para realizar una carga, no impide que otros subprocesos carguen (de hecho, cooperarán, y cada uno realizará su carga en cola por turno, antes de que todos reanuden la ejecución juntos).</p><h3 id="permit-concurrent-loads"><a class="anchorlink" href="#permit-concurrent-loads"><span>5.1</span> <code>permit_concurrent_loads</code></a></h3><p>El Ejecutor adquiere automáticamente un bloqueo <code>running</code> durante la duración de su bloque, y la carga automática sabe cuándo actualizar a un bloqueo <code>load</code> y volver a cambiar a <code>running</code> después.</p><p>Otras operaciones de bloqueo realizadas dentro del bloque del Ejecutor (que incluye todo el código de la aplicación), sin embargo, pueden retener innecesariamente el bloqueo <code>running</code>. Si otro subproceso encuentra una constante que debe cargar automáticamente, esto puede causar un bloqueo.</p><p>Por ejemplo, suponiendo que <code>User</code> aún no esté cargado, lo siguiente causará un bloqueo:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="n">th</span> <span class="o">=</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="no">User</span> <span class="c1"># el subproceso interno espera aquí; no puede cargar</span>
<span class="c1"># User mientras otro subproceso está ejecutándose</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">th</span><span class="p">.</span><span class="nf">join</span> <span class="c1"># el subproceso externo espera aquí, manteniendo el bloqueo 'running'</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="Rails.application.executor.wrap do
th = Thread.new do
Rails.application.executor.wrap do
User # el subproceso interno espera aquí; no puede cargar
# User mientras otro subproceso está ejecutándose
end
end
th.join # el subproceso externo espera aquí, manteniendo el bloqueo 'running'
end
">Copy</button>
</div>
<p>Para evitar este bloqueo, el subproceso externo puede <code>permit_concurrent_loads</code>. Al llamar a este método, el subproceso garantiza que no desreferenciará ninguna constante posiblemente cargada automáticamente dentro del bloque suministrado. La forma más segura de cumplir esa promesa es ponerlo lo más cerca posible de la llamada de bloqueo:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="n">th</span> <span class="o">=</span> <span class="no">Thread</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="no">User</span> <span class="c1"># el subproceso interno puede adquirir el bloqueo 'load',</span>
<span class="c1"># cargar User y continuar</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Dependencies</span><span class="p">.</span><span class="nf">interlock</span><span class="p">.</span><span class="nf">permit_concurrent_loads</span> <span class="k">do</span>
<span class="n">th</span><span class="p">.</span><span class="nf">join</span> <span class="c1"># el subproceso externo espera aquí, pero no tiene bloqueo</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="Rails.application.executor.wrap do
th = Thread.new do
Rails.application.executor.wrap do
User # el subproceso interno puede adquirir el bloqueo 'load',
# cargar User y continuar
end
end
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
th.join # el subproceso externo espera aquí, pero no tiene bloqueo
end
end
">Copy</button>
</div>
<p>Otro ejemplo, usando Concurrent Ruby:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="n">futures</span> <span class="o">=</span> <span class="mi">3</span><span class="p">.</span><span class="nf">times</span><span class="p">.</span><span class="nf">collect</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
<span class="no">Concurrent</span><span class="o">::</span><span class="no">Promises</span><span class="p">.</span><span class="nf">future</span> <span class="k">do</span>
<span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">executor</span><span class="p">.</span><span class="nf">wrap</span> <span class="k">do</span>
<span class="c1"># realiza el trabajo aquí</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">values</span> <span class="o">=</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Dependencies</span><span class="p">.</span><span class="nf">interlock</span><span class="p">.</span><span class="nf">permit_concurrent_loads</span> <span class="k">do</span>
<span class="n">futures</span><span class="p">.</span><span class="nf">collect</span><span class="p">(</span><span class="o">&</span><span class="ss">:value</span><span class="p">)</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="Rails.application.executor.wrap do
futures = 3.times.collect do |i|
Concurrent::Promises.future do
Rails.application.executor.wrap do
# realiza el trabajo aquí
end
end
end
values = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
futures.collect(&:value)
end
end
">Copy</button>
</div>
<h3 id="actiondispatch-debuglocks"><a class="anchorlink" href="#actiondispatch-debuglocks"><span>5.2</span> ActionDispatch::DebugLocks</a></h3><p>Si tu aplicación está bloqueando y crees que el Bloqueo de Carga puede estar involucrado, puedes agregar temporalmente el middleware ActionDispatch::DebugLocks a <code>config/application.rb</code>:</p><div class="interstitial code">
<pre><code class="highlight ruby"><span class="n">config</span><span class="p">.</span><span class="nf">middleware</span><span class="p">.</span><span class="nf">insert_before</span> <span class="no">Rack</span><span class="o">::</span><span class="no">Sendfile</span><span class="p">,</span>
<span class="no">ActionDispatch</span><span class="o">::</span><span class="no">DebugLocks</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="config.middleware.insert_before Rack::Sendfile,
ActionDispatch::DebugLocks
">Copy</button>
</div>
<p>Si luego reinicias la aplicación y vuelves a activar la condición de bloqueo, <code>/rails/locks</code> mostrará un resumen de todos los subprocesos actualmente conocidos por el bloqueo, qué nivel de bloqueo están manteniendo o esperando, y su traza de pila actual.</p><p>Generalmente, un bloqueo será causado por el bloqueo que entra en conflicto con algún otro bloqueo externo o llamada de E/S bloqueante. Una vez que lo encuentres, puedes envolverlo con <code>permit_concurrent_loads</code>.</p>
<hr>
<h3>Comentarios</h3>
<p>
Se te anima a ayudar a mejorar la calidad de esta guía.
</p>
<p>
Por favor contribuye si ves algún error tipográfico o errores fácticos.
Para comenzar, puedes leer nuestra sección de <a href="https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation">contribuciones a la documentación</a>.
</p>
<p>
También puedes encontrar contenido incompleto o cosas que no están actualizadas.
Por favor agrega cualquier documentación faltante para main. Asegúrate de revisar
<a href="https://edgeguides.rubyonrails.org">Guías Edge</a> primero para verificar
si los problemas ya están resueltos o no en la rama principal.
Revisa las <a href="ruby_on_rails_guides_guidelines.html">Guías de Ruby on Rails</a>
para estilo y convenciones.
</p>
<p>
Si por alguna razón detectas algo que corregir pero no puedes hacerlo tú mismo, por favor
<a href="https://github.com/rails/rails/issues">abre un issue</a>.
</p>
<p>Y por último, pero no menos importante, cualquier tipo de discusión sobre la
documentación de Ruby on Rails es muy bienvenida en el <a href="https://discuss.rubyonrails.org/c/rubyonrails-docs">Foro oficial de Ruby on Rails</a>.
</p>
</div>
</div>
</main>
<hr class="hide" />
<footer id="page_footer">
<div class="wrapper">
<p>Este trabajo está bajo una <a href="https://creativecommons.org/licenses/by-sa/4.0/">Licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional</a></p>
<p>"Rails", "Ruby on Rails" y el logotipo de Rails son marcas registradas de David Heinemeier Hansson. Todos los derechos reservados.</p>
<p> Esta traducción fue generada por openAi e <a href="http://latinadeveloper.com/">Isis Harris.</a></p>
</div>
</footer>
</body>
</html>