@@ -5,9 +5,6 @@ defmodule Plausible.Ingestion.WriteBuffer do
55
66 alias Plausible.IngestRepo
77
8- @ lock_timeout :timer . seconds ( 3 )
9- @ lock_interval 100
10-
118 def start_link ( opts ) do
129 GenServer . start_link ( __MODULE__ , opts , name: Keyword . fetch! ( opts , :name ) )
1310 end
@@ -20,54 +17,39 @@ defmodule Plausible.Ingestion.WriteBuffer do
2017 GenServer . call ( server , :flush , :infinity )
2118 end
2219
20+ def flush_async ( server ) do
21+ GenServer . cast ( server , :flush )
22+ end
23+
2324 @ impl true
2425 def init ( opts ) do
2526 name = Keyword . fetch! ( opts , :name )
2627 buffer = opts [ :buffer ] || [ ]
2728 max_buffer_size = opts [ :max_buffer_size ] || default_max_buffer_size ( )
2829 flush_interval_ms = opts [ :flush_interval_ms ] || default_flush_interval_ms ( )
29- lock_timeout_ms = opts [ :lock_timeouts_ms ] || @ lock_timeout
30- lock_interval_ms = opts [ :lock_interval_ms ] || @ lock_interval
30+ on_init = Keyword . get ( opts , :on_init , fn _opts -> % { } end )
3131
3232 Process . flag ( :trap_exit , true )
3333 timer = Process . send_after ( self ( ) , :tick , flush_interval_ms )
3434
35- ^ name = :ets . new ( name , [ :named_table , :set , :public ] )
35+ extra_state = on_init . ( opts )
3636
3737 { :ok ,
38- % {
39- buffer: buffer ,
40- timer: timer ,
41- name: name ,
42- insert_sql: Keyword . fetch! ( opts , :insert_sql ) ,
43- insert_opts: Keyword . fetch! ( opts , :insert_opts ) ,
44- header: Keyword . fetch! ( opts , :header ) ,
45- buffer_size: IO . iodata_length ( buffer ) ,
46- max_buffer_size: max_buffer_size ,
47- flush_interval_ms: flush_interval_ms ,
48- lock_timeout_ms: lock_timeout_ms ,
49- lock_interval_ms: lock_interval_ms
50- } }
51- end
52-
53- @ spec lock ( atom ( ) , pid ( ) , pos_integer ( ) ) :: :ok | { :error , :timeout }
54- def lock ( name , locker \\ self ( ) , timeout ) do
55- true = :ets . insert ( name , { :state , % { locker: locker } } )
56-
57- flush ( name )
58-
59- receive do
60- { :locked , ^ name } -> :ok
61- after
62- timeout -> { :error , :timeout }
63- end
64- end
65-
66- @ spec unlock ( atom ( ) ) :: :ok
67- def unlock ( name ) do
68- true = :ets . insert ( name , { :state , % { locker: nil } } )
69-
70- :ok
38+ Map . merge (
39+ % {
40+ buffer: buffer ,
41+ timer: timer ,
42+ name: name ,
43+ insert_sql: Keyword . fetch! ( opts , :insert_sql ) ,
44+ insert_opts: Keyword . fetch! ( opts , :insert_opts ) ,
45+ on_flush: Keyword . get ( opts , :on_flush , fn _result , state -> state end ) ,
46+ header: Keyword . fetch! ( opts , :header ) ,
47+ buffer_size: IO . iodata_length ( buffer ) ,
48+ max_buffer_size: max_buffer_size ,
49+ flush_interval_ms: flush_interval_ms
50+ } ,
51+ extra_state
52+ ) }
7153 end
7254
7355 @ impl true
@@ -89,6 +71,14 @@ defmodule Plausible.Ingestion.WriteBuffer do
8971 end
9072 end
9173
74+ def handle_cast ( :flush , state ) do
75+ % { timer: timer , flush_interval_ms: flush_interval_ms } = state
76+ Process . cancel_timer ( timer )
77+ do_flush ( state )
78+ new_timer = Process . send_after ( self ( ) , :tick , flush_interval_ms )
79+ { :noreply , % { state | buffer: [ ] , buffer_size: 0 , timer: new_timer } }
80+ end
81+
9282 @ impl true
9383 def handle_info ( :tick , state ) do
9484 do_flush ( state )
@@ -118,44 +108,18 @@ defmodule Plausible.Ingestion.WriteBuffer do
118108 insert_opts: insert_opts ,
119109 insert_sql: insert_sql ,
120110 header: header ,
121- name: name
111+ name: name ,
112+ on_flush: on_flush
122113 } = state
123114
124115 case buffer do
125116 [ ] ->
126- nil
117+ on_flush . ( :empty , state )
127118
128119 _not_empty ->
129120 Logger . notice ( "Flushing #{ buffer_size } byte(s) RowBinary from #{ name } " )
130121 IngestRepo . query! ( insert_sql , [ header | buffer ] , insert_opts )
131- end
132-
133- case :ets . lookup ( state . name , :state ) do
134- [ state: % { locker: pid } ] when is_pid ( pid ) ->
135- send ( pid , { :locked , state . name } )
136- now = System . monotonic_time ( )
137- lock_loop ( state . name , now , state . lock_timeout_ms , state . lock_interval_ms )
138-
139- _ ->
140- :ignore
141- end
142- end
143-
144- defp lock_loop ( name , start , lock_timeout , lock_interval ) do
145- now = System . monotonic_time ( )
146-
147- if now - start <= lock_timeout do
148- Process . sleep ( lock_interval )
149-
150- case :ets . lookup ( name , :state ) do
151- [ state: % { locker: pid } ] when is_pid ( pid ) ->
152- lock_loop ( name , start , lock_timeout , lock_interval )
153-
154- _ ->
155- :pass
156- end
157- else
158- unlock ( name )
122+ on_flush . ( :success , state )
159123 end
160124 end
161125
0 commit comments