@@ -141,12 +141,13 @@ defmodule Postgrex.ReplicationConnection do
141
141
`GenServer`. Read more about them in the `GenServer` docs.
142
142
"""
143
143
144
- use Connection
145
144
require Logger
146
145
import Bitwise
147
146
148
147
alias Postgrex.Protocol
149
148
149
+ @ behaviour :gen_statem
150
+
150
151
@ doc false
151
152
defstruct protocol: nil ,
152
153
state: nil ,
@@ -156,7 +157,7 @@ defmodule Postgrex.ReplicationConnection do
156
157
157
158
## PUBLIC API ##
158
159
159
- @ type server :: GenServer . server ( )
160
+ @ type server :: :gen_statem . server ( )
160
161
@ type state :: term
161
162
@ type ack :: iodata
162
163
@ type query :: iodata
@@ -240,7 +241,7 @@ defmodule Postgrex.ReplicationConnection do
240
241
been replied to should eventually do so. One simple approach is to
241
242
reply to any pending commands on `c:handle_disconnect/1`.
242
243
"""
243
- @ callback handle_call ( term , GenServer . from ( ) , state ) ::
244
+ @ callback handle_call ( term , :gen_statem . from ( ) , state ) ::
244
245
{ :noreply , state }
245
246
| { :noreply , ack , state }
246
247
| { :query , query , state }
@@ -250,7 +251,7 @@ defmodule Postgrex.ReplicationConnection do
250
251
Callback for `:query` outputs.
251
252
252
253
If any callback returns `{:query, iodata, state}`,
253
- then this callback will be immediatelly called with
254
+ then this callback will be immediately called with
254
255
the result of the query. Please note that even though
255
256
replication connections use the simple query protocol,
256
257
Postgres currently limits them to single command queries.
@@ -274,13 +275,13 @@ defmodule Postgrex.ReplicationConnection do
274
275
@ doc """
275
276
Replies to the given `call/3`.
276
277
"""
277
- defdelegate reply ( client , reply ) , to: GenServer
278
+ defdelegate reply ( client , reply ) , to: :gen_statem
278
279
279
280
@ doc """
280
281
Calls the given replication server.
281
282
"""
282
283
def call ( server , message , timeout \\ 5000 ) do
283
- with { __MODULE__ , reason } <- GenServer . call ( server , message , timeout ) do
284
+ with { __MODULE__ , reason } <- :gen_statem . call ( server , message , timeout ) do
284
285
exit ( { reason , { __MODULE__ , :call , [ server , message , timeout ] } } )
285
286
end
286
287
end
@@ -339,10 +340,34 @@ defmodule Postgrex.ReplicationConnection do
339
340
@ spec start_link ( module ( ) , term ( ) , Keyword . t ( ) ) ::
340
341
{ :ok , pid } | { :error , Postgrex.Error . t ( ) | term }
341
342
def start_link ( module , arg , opts ) do
342
- { server_opts , opts } = Keyword . split ( opts , [ :name ] )
343
+ { name , opts } = Keyword . pop ( opts , :name )
343
344
opts = Keyword . put_new ( opts , :sync_connect , true )
344
345
connection_opts = Postgrex.Utils . default_opts ( opts )
345
- Connection . start_link ( __MODULE__ , { module , arg , connection_opts } , server_opts )
346
+ start_args = { module , arg , connection_opts }
347
+
348
+ case name do
349
+ nil ->
350
+ :gen_statem . start_link ( __MODULE__ , start_args , [ ] )
351
+
352
+ atom when is_atom ( atom ) ->
353
+ :gen_statem . start_link ( { :local , atom } , __MODULE__ , start_args , [ ] )
354
+
355
+ { :global , _term } = tuple ->
356
+ :gen_statem . start_link ( tuple , __MODULE__ , start_args , [ ] )
357
+
358
+ { :via , via_module , _term } = tuple when is_atom ( via_module ) ->
359
+ :gen_statem . start_link ( tuple , __MODULE__ , start_args , [ ] )
360
+
361
+ other ->
362
+ raise ArgumentError , """
363
+ expected :name option to be one of the following:
364
+ * nil
365
+ * atom
366
+ * {:global, term}
367
+ * {:via, module, term}
368
+ Got: #{ inspect ( other ) }
369
+ """
370
+ end
346
371
end
347
372
348
373
@ doc """
@@ -409,7 +434,14 @@ defmodule Postgrex.ReplicationConnection do
409
434
410
435
## CALLBACKS ##
411
436
437
+ @ state :no_state
438
+
439
+ @ doc false
440
+ @ impl :gen_statem
441
+ def callback_mode , do: :handle_event_function
442
+
412
443
@ doc false
444
+ @ impl :gen_statem
413
445
def init ( { mod , arg , opts } ) do
414
446
case mod . init ( arg ) do
415
447
{ :ok , mod_state } ->
@@ -433,42 +465,44 @@ defmodule Postgrex.ReplicationConnection do
433
465
put_opts ( opts )
434
466
435
467
if opts [ :sync_connect ] do
436
- case connect ( : init, state ) do
437
- { :ok , _ } = ok -> ok
438
- { :backoff , _ , _ } = backoff -> backoff
439
- { :stop , reason , _ } -> { : stop, reason }
468
+ case handle_event ( :internal , { :connect , : init} , @ state , state ) do
469
+ { :keep_state , state } -> { :ok , @ state , state }
470
+ { :keep_state , state , actions } -> { :ok , @ state , state , actions }
471
+ { :stop , _reason , _state } = stop -> stop
440
472
end
441
473
else
442
- { :connect , :init , state }
474
+ { :ok , @ state , state , { :next_event , :internal , { :connect , :init } } }
443
475
end
444
476
end
445
477
end
446
478
447
479
@ doc false
448
- def connect ( _ , % { state: { mod , mod_state } } = s ) do
480
+ @ impl :gen_statem
481
+ def handle_event ( type , content , state , s )
482
+
483
+ def handle_event ( { :timeout , :backoff } , nil , @ state , s ) do
484
+ { :keep_state , s , { :next_event , :internal , { :connect , :backoff } } }
485
+ end
486
+
487
+ def handle_event ( :internal , { :connect , _info } , @ state , % { state: { mod , mod_state } } = s ) do
449
488
case Protocol . connect ( opts ( ) ) do
450
489
{ :ok , protocol } ->
451
- s = % { s | protocol: protocol }
452
-
453
- with { :noreply , s } <- maybe_handle ( mod , :handle_connect , [ mod_state ] , s ) do
454
- { :ok , s }
455
- end
490
+ maybe_handle ( mod , :handle_connect , [ mod_state ] , % { s | protocol: protocol } )
456
491
457
492
{ :error , reason } ->
458
493
if s . auto_reconnect do
459
- { :backoff , s . reconnect_backoff , s }
494
+ { :keep_state , s , { { :timeout , : backoff} , s . reconnect_backoff , nil } }
460
495
else
461
496
{ :stop , reason , s }
462
497
end
463
498
end
464
499
end
465
500
466
- def handle_call ( msg , from , % { state: { mod , mod_state } } = s ) do
501
+ def handle_event ( { :call , from } , msg , @ state , % { state: { mod , mod_state } } = s ) do
467
502
handle ( mod , :handle_call , [ msg , from , mod_state ] , from , s )
468
503
end
469
504
470
- @ doc false
471
- def handle_info ( msg , % { protocol: protocol , streaming: streaming } = s ) do
505
+ def handle_event ( :info , msg , @ state , % { protocol: protocol , streaming: streaming } = s ) do
472
506
case Protocol . handle_copy_recv ( msg , streaming , protocol ) do
473
507
{ :ok , copies , protocol } ->
474
508
handle_data ( copies , % { s | protocol: protocol } )
@@ -482,16 +516,18 @@ defmodule Postgrex.ReplicationConnection do
482
516
end
483
517
end
484
518
485
- defp handle_data ( [ ] , s ) , do: { :noreply , s }
519
+ ## Helpers
520
+
521
+ defp handle_data ( [ ] , s ) , do: { :keep_state , s }
486
522
487
523
defp handle_data ( [ :copy_done | copies ] , % { state: { mod , mod_state } } = s ) do
488
- with { :noreply , s } <- handle ( mod , :handle_data , [ :done , mod_state ] , nil , s ) do
524
+ with { :keep_state , s } <- handle ( mod , :handle_data , [ :done , mod_state ] , nil , s ) do
489
525
handle_data ( copies , % { s | streaming: nil } )
490
526
end
491
527
end
492
528
493
529
defp handle_data ( [ copy | copies ] , % { state: { mod , mod_state } } = s ) do
494
- with { :noreply , s } <- handle ( mod , :handle_data , [ copy , mod_state ] , nil , s ) do
530
+ with { :keep_state , s } <- handle ( mod , :handle_data , [ copy , mod_state ] , nil , s ) do
495
531
handle_data ( copies , s )
496
532
end
497
533
end
@@ -500,20 +536,20 @@ defmodule Postgrex.ReplicationConnection do
500
536
if function_exported? ( mod , fun , length ( args ) ) do
501
537
handle ( mod , fun , args , nil , s )
502
538
else
503
- { :noreply , s }
539
+ { :keep_state , s }
504
540
end
505
541
end
506
542
507
543
defp handle ( mod , fun , args , from , % { streaming: streaming } = s ) do
508
544
case apply ( mod , fun , args ) do
509
545
{ :noreply , mod_state } ->
510
- { :noreply , % { s | state: { mod , mod_state } } }
546
+ { :keep_state , % { s | state: { mod , mod_state } } }
511
547
512
548
{ :noreply , replies , mod_state } ->
513
549
s = % { s | state: { mod , mod_state } }
514
550
515
551
case Protocol . handle_copy_send ( replies , s . protocol ) do
516
- :ok -> { :noreply , s }
552
+ :ok -> { :keep_state , s }
517
553
{ error , reason , protocol } -> reconnect_or_stop ( error , reason , protocol , s )
518
554
end
519
555
@@ -523,7 +559,7 @@ defmodule Postgrex.ReplicationConnection do
523
559
524
560
with { :ok , protocol } <- Protocol . handle_streaming ( query , s . protocol ) ,
525
561
{ :ok , protocol } <- Protocol . checkin ( protocol ) do
526
- { :noreply , % { s | protocol: protocol , streaming: max_messages } }
562
+ { :keep_state , % { s | protocol: protocol , streaming: max_messages } }
527
563
else
528
564
{ error_or_disconnect , reason , protocol } ->
529
565
reconnect_or_stop ( error_or_disconnect , reason , protocol , s )
@@ -552,21 +588,24 @@ defmodule Postgrex.ReplicationConnection do
552
588
defp stream_in_progress ( command , mod , mod_state , from , s ) do
553
589
Logger . warning ( "received #{ command } while stream is already in progress" )
554
590
from && reply ( from , { __MODULE__ , :stream_in_progress } )
555
- { :noreply , % { s | state: { mod , mod_state } } }
591
+ { :keep_state , % { s | state: { mod , mod_state } } }
556
592
end
557
593
558
594
defp reconnect_or_stop ( error , reason , protocol , % { auto_reconnect: false } = s )
559
595
when error in [ :error , :disconnect ] do
560
596
% { state: { mod , mod_state } } = s
561
- { :noreply , s } = maybe_handle ( mod , :handle_disconnect , [ mod_state ] , % { s | protocol: protocol } )
597
+
598
+ { :keep_state , s } =
599
+ maybe_handle ( mod , :handle_disconnect , [ mod_state ] , % { s | protocol: protocol } )
600
+
562
601
{ :stop , reason , s }
563
602
end
564
603
565
604
defp reconnect_or_stop ( error , _reason , _protocol , % { auto_reconnect: true } = s )
566
605
when error in [ :error , :disconnect ] do
567
606
% { state: { mod , mod_state } } = s
568
- { :noreply , s } = maybe_handle ( mod , :handle_disconnect , [ mod_state ] , s )
569
- { :connect , :reconnect , % { s | streaming: nil } }
607
+ { :keep_state , s } = maybe_handle ( mod , :handle_disconnect , [ mod_state ] , s )
608
+ { :keep_state , % { s | streaming: nil } , { :next_event , :internal , { :connect , :reconnect } } }
570
609
end
571
610
572
611
defp opts ( ) , do: Process . get ( __MODULE__ )
0 commit comments