11create or replace package body ut_output_pipe_helper is
22
3+ -- private
34
45 type tt_pipe_buffer is table of varchar2(4000);
56
67 type t_output_buffer is record(
7- to_close boolean := false,
8+ to_flush boolean := false,
89 data tt_pipe_buffer
910 );
1011
11- type tt_outputs_buffer is table of t_output_buffer index by varchar2(128) ;
12+ type tt_outputs_buffer is table of t_output_buffer index by t_output_id ;
1213
1314 g_outputs_buffer tt_outputs_buffer;
1415
15- function send_from_buffer(a_output_id varchar2, a_buffer in out nocopy tt_pipe_buffer) return boolean is
16+ --sends all data from buffer to pipe and returns false if timeout occured
17+ function send_from_buffer(a_output_id t_output_id, a_buffer in out nocopy tt_pipe_buffer, a_timeout_seconds naturaln := 0) return boolean is
1618 l_is_successful boolean := true;
1719 l_first_item integer;
1820 l_last_item integer;
@@ -25,7 +27,7 @@ create or replace package body ut_output_pipe_helper is
2527 -- exit loop on timeout
2628 while i is not null loop
2729 dbms_pipe.pack_message( a_buffer(i) );
28- l_is_successful := dbms_pipe.send_message(a_output_id, 0 ) = 0;
30+ l_is_successful := dbms_pipe.send_message(a_output_id, a_timeout_seconds ) = 0;
2931 exit when not l_is_successful;
3032 l_last_item := i;
3133 i := a_buffer.next(i);
@@ -35,93 +37,117 @@ create or replace package body ut_output_pipe_helper is
3537 if not l_is_successful then
3638 a_buffer.delete(l_first_item, i-1);
3739 else
38- a_buffer.delete() ;
40+ a_buffer.delete;
3941 end if;
4042 dbms_pipe.reset_buffer;
4143 return l_is_successful;
4244 end if;
4345 return false;
4446 end;
4547
46- function send_from_buffer(a_output_id varchar2) return boolean is
48+ --sends all data from an output buffer to pipe and returns false if timeout occured
49+ function send_from_buffer(a_output_id t_output_id, a_timeout_seconds naturaln := 0) return boolean is
4750 begin
4851 if g_outputs_buffer.exists(a_output_id) then
49- return send_from_buffer(a_output_id, g_outputs_buffer(a_output_id).data);
52+ return send_from_buffer(a_output_id, g_outputs_buffer(a_output_id).data, a_timeout_seconds );
5053 end if;
5154 return false;
5255 end;
5356
54- --sends a message to a named pipe
55- --and if sending fails it writes the message to the end of the buffer for pipe
56- procedure send(a_output_id varchar2, a_text t_pipe_item) is
57- l_is_successful boolean;
57+ --iterates through all the output buffers that were not purged
58+ --for each buffer tries to send the content of the buffer
59+ --if all content was sent, buffer is recycled (removed)
60+ function flush_buffers(a_timeout_seconds naturaln := 0) return boolean is
61+ l_output_id t_output_id;
62+ l_output_id_to_delete t_output_id;
63+ l_pipe_removal_status integer;
64+ l_timed_out boolean := false;
5865 begin
59- buffer(a_output_id, a_text);
60- l_is_successful := send_from_buffer(a_output_id);
66+ l_output_id := g_outputs_buffer.first;
67+ while l_output_id is not null loop
68+ l_output_id_to_delete := null;
69+ if send_from_buffer(l_output_id, a_timeout_seconds) = true then
70+ l_output_id_to_delete := l_output_id;
71+ else
72+ l_timed_out := true;
73+ end if;
74+ l_output_id := g_outputs_buffer.next(l_output_id);
75+
76+ if l_output_id_to_delete is not null then
77+ g_outputs_buffer.delete(l_output_id_to_delete);
78+ end if;
79+
80+ end loop;
81+ return not l_timed_out;
82+ end;
83+
84+ --check if all the buffers are waiting to be flushed
85+ function all_buffers_to_flush return boolean is
86+ l_output_id t_output_id;
87+ begin
88+ l_output_id := g_outputs_buffer.first;
89+ while l_output_id is not null loop
90+ if not g_outputs_buffer(l_output_id).to_flush then
91+ return false;
92+ end if;
93+ l_output_id := g_outputs_buffer.next(l_output_id);
94+ end loop;
95+ return true;
6196 end;
6297
63- --writes the message to the end of the buffer for pipe
64- procedure buffer(a_output_id varchar2, a_text t_pipe_item) is
98+ --remove all the pipes anf purges all the buffers
99+ procedure purge is
100+ l_pipe_removal_status integer;
101+ l_output_id t_output_id;
102+ begin
103+ l_output_id := g_outputs_buffer.first;
104+ while l_output_id is not null loop
105+ l_output_id := g_outputs_buffer.next(l_output_id);
106+ end loop;
107+ g_outputs_buffer.delete;
108+ end;
109+
110+ --writes the message to the end of the buffer
111+ procedure buffer(a_output_id t_output_id, a_text t_pipe_item) is
65112 begin
66113 if not g_outputs_buffer.exists(a_output_id) then
67114 g_outputs_buffer(a_output_id).data := tt_pipe_buffer();
115+ g_outputs_buffer(a_output_id).to_flush := false;
68116 end if;
69117 g_outputs_buffer(a_output_id).data.extend;
70118 g_outputs_buffer(a_output_id).data(g_outputs_buffer(a_output_id).data.last) := a_text;
71119 end;
72120
73- procedure cleanup_outputs_to_close is
74- l_output_id varchar2(128);
75- l_output_id_to_delete varchar2(128);
121+
122+ ---public
123+
124+ --adds message to pipe buffer and tries to sent all messages from the buffer
125+ --exists immediately when sending timesout (pipe full)
126+ --the messages that were sent are removed from buffer
127+ procedure send(a_output_id t_output_id, a_text t_pipe_item) is
128+ l_is_successful boolean;
76129 begin
77- l_output_id := g_outputs_buffer.first;
78- while i is not null loop
79- l_output_id_to_delete := null;
80- if g_outputs_buffer(l_output_id).to_close then
81- if send_from_buffer(l_output_id) = true then
82- l_output_id_to_delete := l_output_id;
83- end if;
84- end if;
85- l_output_id := g_outputs_buffer.next(l_output_id);
86- g_outputs_buffer.delete(l_output_id_to_delete);
87- end loop;
130+ buffer(a_output_id, a_text);
131+ l_is_successful := send_from_buffer(a_output_id);
88132 end;
89133
90- --procedure cleanup_aged_pipes() - check for pipes that are aged out.
91-
92-
93- --registers a close request and tries to close a pipe
94- --by first sending out all messages remaining in the buffers
95- --the procedure holds infomrmation about closure requests
96- --so that if close is not possble as buffer was not yet flusehd
97- --it will proceed to other close requests
98- --scenario with failure:
99- -- two buffers used: output_1, output_2
100- -- call is made to close output_1
101- -- register the close request
102- -- send all messaged from output_1 buffer
103- -- if buffer not empty and timeout
104- -- go to output_2
105- -- if output_2 is still open, return
106- -- so the outout_1 close request is in a pending state
107- -- next a call should be made from framework to close output_2
108- -- register the close request
109- -- send all messaged from output_2 buffer
110- -- if buffer not empty and timeout
111- -- go to output_1
112- -- if output_1 has a pending close
113- -- try to send from output_1 buffer
114- -- if timeout -> raise
115- --scenario with success:
116- -- two buffers used: output_1, output_2
117- procedure request_close(a_output_id varchar2, a_text varchar2) is
134+ --marks a buffer as ready to be flushed
135+ --tries to flush all the data from all the outputs buffers to the pipes immediately
136+ --in case, all buffers outputs are to be flused, it will try with a timeout.
137+ procedure flush(a_output_id t_output_id, a_timeout_seconds naturaln := gc_flush_timeout_seconds) is
118138 begin
119- cleanup_outputs_to_close;
120- --if there is any output in a state to_close = false
121- --exit
122- --else loop with a wait time of 20 seconds for each to_close output
139+ if g_outputs_buffer.exists(a_output_id) then
140+ g_outputs_buffer(a_output_id).to_flush := true;
141+ end if;
123142
124- --if unable to send all messages after timeout, then finish?
143+ if (flush_buffers() = false) and all_buffers_to_flush then
144+ --try as many times as there are seconds for timeout
145+ --each time try with one second delay
146+ for i in 1 .. a_timeout_seconds loop
147+ exit when flush_buffers(a_timeout_seconds => 1) = true;
148+ end loop;
149+ purge();
150+ end if;
125151
126152 end;
127153
0 commit comments