Loading...
Searching...
No Matches
mv_jobwaitfor.sas
Go to the documentation of this file.
1/**
2 @file
3 @brief Takes a table of running jobs and waits for ANY/ALL of them to complete
4 @details Will poll `/jobs/{jobId}/state` at set intervals until ANY or ALL
5 jobs are completed. Completion is determined by reference to the returned
6 _state_, as per the following table:
7
8 | state | Wait? | Notes|
9 |-----------|-------|------|
10 | idle | yes | We assume processing will continue. Beware of idle sessions with no code submitted! |
11 | pending | yes | Job is preparing to run |
12 | running | yes | Job is running|
13 | canceled | no | Job was cancelled|
14 | completed | no | Job finished - does not mean it was successful. Check stateDetails|
15 | failed | no | Job failed to execute, could be a problem when calling the apis|
16
17
18 ## Example
19
20 First, compile the macros:
21
22 filename mc url
23 "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
24 %inc mc;
25
26 Next, create a job (in this case, as a web service):
27
28 filename ft15f001 temp;
29 parmcards4;
30 data ;
31 rand=ranuni(0)*1000000;
32 do x=1 to rand;
33 y=rand*x;
34 output;
35 end;
36 run;
37 ;;;;
38 %mv_createwebservice(path=/Public/temp,name=demo)
39
40 Then, execute the job,multiple times, and wait for them all to finish:
41
42 %mv_jobexecute(path=/Public/temp,name=demo,outds=work.ds1)
43 %mv_jobexecute(path=/Public/temp,name=demo,outds=work.ds2)
44 %mv_jobexecute(path=/Public/temp,name=demo,outds=work.ds3)
45 %mv_jobexecute(path=/Public/temp,name=demo,outds=work.ds4)
46
47 data work.jobs;
48 set work.ds1 work.ds2 work.ds3 work.ds4;
49 where method='GET' and rel='state';
50 run;
51
52 %mv_jobwaitfor(ALL,inds=work.jobs,outds=work.jobstates)
53
54 Delete the job:
55
56 %mv_deletejes(path=/Public/temp,name=demo)
57
58 @param [in] access_token_var= The global macro variable to contain the access
59 token
60 @param [in] grant_type= valid values:
61
62 - password
63 - authorization_code
64 - detect - will check if access_token exists, if not will use sas_services
65 if a SASStudioV session else authorization_code. Default option.
66 - sas_services - will use oauth_bearer=sas_services
67
68 @param [in] action=Either ALL (to wait for every job) or ANY (if one job
69 completes, processing will continue). Default=ALL.
70 @param [in] inds= The input dataset containing the list of job uris, in the
71 following format: `/jobExecution/jobs/&JOBID.` (a trailing `/state`
72 suffix, as returned by the mv_jobexecute state link, is also
73 accepted and will be stripped) and the corresponding job name.
74 The uri should be in a `uri` variable, and the job path/name
75 should be in a `_program` variable.
76 @param [in] raise_err=0 Set to 1 to raise SYSCC when a job does not complete
77 succcessfully
78 @param [in] mdebug= set to 1 to enable DEBUG messages
79 @param [out] outds= The output dataset containing the list of states by job
80 (default=work.mv_jobexecute)
81 @param [out] outref= A fileref to which the spawned job logs should be
82 appended.
83
84 <h4> SAS Macros </h4>
85 @li mp_abort.sas
86 @li mf_existvar.sas
87 @li mf_nobs.sas
88 @li mf_getplatform.sas
89 @li mf_getuniquefileref.sas
90 @li mf_getuniquelibref.sas
91 @li mv_getjoblog.sas
92
93 @version VIYA V.03.04
94 @author Allan Bowe, source: https://github.com/sasjs/core
95
96 <h4> Dependencies </h4>
97 @li mp_abort.sas
98 @li mf_getplatform.sas
99 @li mf_getuniquefileref.sas
100 @li mf_getuniquelibref.sas
101 @li mf_existvar.sas
102 @li mf_nobs.sas
103 @li mv_getjoblog.sas
104
105**/
106
107%macro mv_jobwaitfor(action
108 ,access_token_var=ACCESS_TOKEN
109 ,grant_type=sas_services
110 ,inds=0
111 ,outds=work.mv_jobwaitfor
112 ,outref=0
113 ,raise_err=0
114 ,mdebug=0
115 );
116%local dbg;
117%if &mdebug=1 %then %do;
118 %put &sysmacroname entry vars:;
119 %put _local_;
120%end;
121%else %let dbg=*;
122
123%local oauth_bearer;
124%if &grant_type=detect %then %do;
125 %if %symexist(&access_token_var) %then %let grant_type=authorization_code;
126 %else %let grant_type=sas_services;
127%end;
128%if &grant_type=sas_services %then %do;
129 %let oauth_bearer=oauth_bearer=sas_services;
130 %let &access_token_var=;
131%end;
132
133%mp_abort(iftrue=(&grant_type ne authorization_code and &grant_type ne password
134 and &grant_type ne sas_services
135 )
136 ,mac=&sysmacroname
137 ,msg=%str(Invalid value for grant_type: &grant_type)
138)
139
140%mp_abort(iftrue=("&inds"="0")
141 ,mac=&sysmacroname
142 ,msg=%str(input dataset not provided)
143)
144%mp_abort(iftrue=(%mf_existvar(&inds,uri)=0)
145 ,mac=&sysmacroname
146 ,msg=%str(The URI variable was not found in the input dataset(&inds))
147)
148%mp_abort(iftrue=(%mf_existvar(&inds,_program)=0)
149 ,mac=&sysmacroname
150 ,msg=%str(The _PROGRAM variable was not found in the input dataset(&inds))
151)
152
153%if %mf_nobs(&inds)=0 %then %do;
154 %put NOTE: Zero observations in &inds, &sysmacroname will now exit;
155 %return;
156%end;
157
158options noquotelenmax;
159%local base_uri; /* location of rest apis */
160%let base_uri=%mf_getplatform(VIYARESTAPI);
161
162data _null_;
163 length jobparams $32767;
164 set &inds end=last;
165 /* strip any trailing /state suffix - the poll below GETs the plain
166 job uri (whose root object carries the state field), and
167 downstream consumers (mv_getjoblog, the outds) expect the plain
168 job uri. Do not truncate: job uris are not a fixed length (a
169 definition uri passed in error must fail loudly, not be silently
170 corrupted into a 404). */
171 if length(strip(uri))>6 and substr(strip(uri),length(strip(uri))-5)='/state'
172 then uri=substr(strip(uri),1,length(strip(uri))-6);
173 call symputx(cats('joburi',_n_),strip(uri),'l');
174 call symputx(cats('jobname',_n_),_program,'l');
175 call symputx(cats('jobparams',_n_),jobparams,'l');
176 if last then call symputx('uricnt',_n_,'l');
177run;
178
179%local runcnt;
180%if &action=ALL %then %let runcnt=&uricnt;
181%else %if &action=ANY %then %let runcnt=1;
182%else %let runcnt=&uricnt;
183
184%local fname0 ;
185%let fname0=%mf_getuniquefileref();
186
187data &outds;
188 format _program uri $128. state $32. stateDetails $32. timestamp datetime19.
189 jobparams $32767.;
190 call missing (of _all_);
191 stop;
192run;
193
194%local i;
195%do i=1 %to &uricnt;
196 %if "&&joburi&i" ne "0" %then %do;
197 proc http method='GET' out=&fname0 &oauth_bearer url="&base_uri/&&joburi&i";
198 headers "Accept"="application/json"
199 %if &grant_type=authorization_code %then %do;
200 "Authorization"="Bearer &&&access_token_var"
201 %end; ;
202 run;
203 %if &SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201
204 %then %do;
205 data _null_;infile &fname0;input;putlog _infile_;run;
206 %mp_abort(mac=&sysmacroname
207 ,msg=%str(&SYS_PROCHTTP_STATUS_CODE &SYS_PROCHTTP_STATUS_PHRASE)
208 )
209 %end;
210
211 %let status=notset;
212
213 %local libref1;
214 %let libref1=%mf_getuniquelibref();
215 libname &libref1 json fileref=&fname0;
216
217 data _null_;
218 length state stateDetails $32;
219 set &libref1..root;
220 call symputx('status',state,'l');
221 call symputx('stateDetails',stateDetails,'l');
222 run;
223
224 libname &libref1 clear;
225
226 %if &status=completed or &status=failed or &status=canceled %then %do;
227 /* &&joburi&i is already the plain job uri (any /state suffix was
228 stripped on input) - use it as-is. */
229 proc sql;
230 insert into &outds set
231 _program="&&jobname&i",
232 uri="&&joburi&i",
233 state="&status",
234 stateDetails=symget("stateDetails"),
235 timestamp=datetime(),
236 jobparams=symget("jobparams&i");
237 /* fetch log */
238 %if %str(&outref) ne 0 %then %do;
239 %mv_getjoblog(uri=&&joburi&i,outref=&outref,mdebug=&mdebug)
240 %end;
241 %let joburi&i=0; /* do not re-check */
242 %end;
243 %else %if &status=idle or &status=pending or &status=running %then %do;
244 data _null_;
245 call sleep(1,1);
246 run;
247 %end;
248 %else %do;
249 %mp_abort(mac=&sysmacroname
250 ,msg=%str(status &status not expected!!)
251 )
252 %end;
253
254 %if (&raise_err) %then %do;
255 %if (&status = canceled or &status = failed or %length(&stateDetails)>0)
256 %then %do;
257 %if ("&stateDetails" = "%str(war)ning") %then %let SYSCC=4;
258 %else %let SYSCC=5;
259 %put %str(ERR)OR: Job &&jobname&i. did not complete. &stateDetails;
260 %return;
261 %end;
262 %end;
263
264 %end;
265 %if &i=&uricnt %then %do;
266 %local goback;
267 %let goback=0;
268 proc sql noprint;
269 select count(*) into:goback from &outds;
270 %if &goback lt &runcnt %then %let i=0;
271 %end;
272%end;
273
274%if &mdebug=1 %then %do;
275 %put &sysmacroname exit vars:;
276 %put _local_;
277%end;
278%else %do;
279 /* clear refs */
280 filename &fname0 clear;
281%end;
282%mend mv_jobwaitfor;