Hi Folks,
I have a piece of SAS code that updates for a given user in Metadata (and a given Authentication doman of this particular user) its Login-Credentials:
%macro update_login_in_metada(name, pwd, auth);
data _null_;
length uri_domain $256.;
length uri_user $256.;
length uri_login $256.;
length name_domain $200.;
length name_user $200.;
* initialize "uri*" and "name_*" variables;
call missing(of uri_: name_:);
* initialize variable "nobj1";
nobj1 = -1;
* write number of person objects found in metadata into "nobj1";
nobj1 = metadata_getnobj("omsobj:Person?@Id contains '.'", 1, uri_user);
* loop through all found person objects;
if nobj1 > 0 then
do;
do i = 1 to nobj1;
nobj2 = metadata_getnobj("omsobj:Person?@Id contains '.'", i, uri_user);
rc1 = metadata_getattr(uri_user, "Name", name_user);
* lookup person object;
if name_user = "&name." then
do;
* get corresponding Login-Account of Auth-Domain;
nobj3 = metadata_getnasn(uri_user, "Logins", 1, uri_login);
* loop through all Login-Accounts of particular user;
if nobj3 > 0 then
do;
do j = 1 to nobj3;
nobj4 = metadata_getnasn(uri_user, "Logins", j, uri_login);
nobj5 = metadata_getnasn(uri_login, "Domain", 1, uri_domain);
rc2 = metadata_getattr(uri_domain, "Name", name_domain);
if name_domain = "&auth." then /* update password for selected Auth-Domain*/
do;
rc3 = metadata_setattr(uri_login, "Password", "&pwd.");
end;
end;
end;
end;
end;
end;
run;
%mend update_login_in_metada;
It takes approximately 50 seconds for one user to run this step:
%update_login_in_metada(johndoe, abc123, DBAuth)
To me it seems, that the Data Step Metadata Functions are very slow...
I know of the Metadata Procedure as a potential alternative way to update metadata. However, I do not know, how to set up/write the appropriate xml file structure as input to the procedure.
Also, is the Metadata Procedure faster than the Data Step Metadata Functions?
Any help would be highly appreciated,
FK21
You could simplify your macro:
%macro update_login_in_metada(name, pwd, auth);
data _null_;
length uri_login $ 250;
uri_login = cats("omsobj:Login?Login[AssociatedIdentity/Person[@Name = '&name']]",
"[Domain/AuthenticationDomain[@Name = '&auth']]"
);
rc = metadata_setattr(uri_login, "Password", "&pwd.");
put rc=;
run;
%mend;
if rc ^= 0 then no user exists with a login in the given auth-domain.
I think you could improve on the logic you have posted by dropping out of your looping once the password has been updated. Try this:
%macro update_login_in_metada(name, pwd, auth);
data _null_;
length uri_domain $256.;
length uri_user $256.;
length uri_login $256.;
length name_domain $200.;
length name_user $200.;
* initialize "uri*" and "name_*" variables;
call missing(of uri_: name_:);
* initialize variable "nobj1";
nobj1 = -1;
* write number of person objects found in metadata into "nobj1";
nobj1 = metadata_getnobj("omsobj:Person?@Id contains '.'", 1, uri_user);
Password_Updated = 'N';
* loop through all found person objects;
if nobj1 > 0 then
do;
do i = 1 to nobj1 until (Password_Updated = 'Y');
nobj2 = metadata_getnobj("omsobj:Person?@Id contains '.'", i, uri_user);
rc1 = metadata_getattr(uri_user, "Name", name_user);
* lookup person object;
if name_user = "&name." then
do;
* get corresponding Login-Account of Auth-Domain;
nobj3 = metadata_getnasn(uri_user, "Logins", 1, uri_login);
* loop through all Login-Accounts of particular user;
if nobj3 > 0 then
do;
do j = 1 to nobj3;
nobj4 = metadata_getnasn(uri_user, "Logins", j, uri_login);
nobj5 = metadata_getnasn(uri_login, "Domain", 1, uri_domain);
rc2 = metadata_getattr(uri_domain, "Name", name_domain);
if name_domain = "&auth." then /* update password for selected Auth-Domain*/
do;
rc3 = metadata_setattr(uri_login, "Password", "&pwd.");
Password_Updated = 'Y';
end;
end;
end;
end;
end;
end;
run;
%mend update_login_in_metada;
Thanks, this is definitely an improvement in logical terms!
However, the runtime wasn't really significantly lowered (24 seconds versus 23 seconds).
But I wonder, why is there such a big margin of error regarding the runtime? Yesterday I ran the exaxt same code and it took 47 seconds. Today it is only 24/23 seconds.
Of course, there could be different amounts of traffic regarding the server, but I do have the feeling, that the metadata handling is slow. I already ran the CLI admin tools to reorg and clean the metadata, but still it is very slow.
Is there another action I could pursue to speed up metadata access?
@FK21 - The speed of reading a SAS metadata repository will likely depend on a number of factors. How big is your repository? The bigger it is the slower it will be to read it. Do you regularly compact its size? There is a tool in SAS Management Console for doing this. Also the repository is stored on your metadata server, but you are reading it from your application server so there is likely a lot of network traffic necessary to read it. And the busier your SAS installation is, the slower it will be to read the repository. How long does an after-hours read take?
@SASKiwi : In deed, there are several factors to consider. I am under the impression, though, that we do have an issue with our production metadata.
As you suggested, I ran the code after the usual business hours in two of our environments: production and test
Statistics of Test environment:
NOTE: DATA statement used (Total process time):
real time 2.58 seconds
user cpu time 0.01 seconds
system cpu time 0.01 seconds
memory 1132.59k
OS Memory 20892.00k
Timestamp 03/21/2025 06:45:31 AM
Step Count 5 Switch Count 164
Page Faults 20
Page Reclaims 281
Page Swaps 0
Voluntary Context Switches 975
Involuntary Context Switches 3
Block Input Operations 0
Block Output Operations 0
Statistics of Production environment:
NOTE: DATA statement used (Total process time):
real time 24.40 seconds
user cpu time 0.12 seconds
system cpu time 0.02 seconds
memory 7216.50k
OS Memory 27548.00k
Timestamp 03/21/2025 06:47:40 AM
Step Count 5 Switch Count 1364
Page Faults 0
Page Reclaims 1763
Page Swaps 0
Voluntary Context Switches 7944
Involuntary Context Switches 104
Block Input Operations 0
Block Output Operations 0
As you can see, in production it took ~12 times the amount of time it took in test.
Also, I compared the size of the <config-dir>/SASMeta/MetadataServer/MetadataRepositories/Foundation/ directories for PROD and TEST: 250 MB (PROD) vs. 248 MB (TEST).
Regarding the factor of network traffic, I also ran this code directly in a Workspaceserver session on the MetadataServer machine on PROD and TEST, respectively. The "deviation" of roughly 12 times remains!
Therefore, I would rule out the following factors:
This only leaves me with the topic of "compacting the size of the metadata repo". What excatly do you mean with "tool in SMC"? Do you mean the Metadata Analysis/Repair Tools?
@FK21 - I was referring to the "Reorganize" option when you run a manual backup. This is supposed to compact the repository.
You could simplify your macro:
%macro update_login_in_metada(name, pwd, auth);
data _null_;
length uri_login $ 250;
uri_login = cats("omsobj:Login?Login[AssociatedIdentity/Person[@Name = '&name']]",
"[Domain/AuthenticationDomain[@Name = '&auth']]"
);
rc = metadata_setattr(uri_login, "Password", "&pwd.");
put rc=;
run;
%mend;
if rc ^= 0 then no user exists with a login in the given auth-domain.
@andreas_lds : this is awesome code! The only problem I have at the moment:
when executing this:
%update_login_in_metada(TESTUSR7, someString1234, SPDSAuth);
I get
rc=-3 (meaning that no objects match the URI)
However, I know, that this object with the given Authdomain exists as you can see in the scrennshot below:
So I guess, the omsobj-query string does not fully fit yet or am I missing something?
Interesting, omsobj-query should work, but this was a rather long day for me, so maybe this hides the error.
Some ideas to debug:
num_users = metadata_getnobj("omsobj:Login?Login[AssociatedIdentity/Person[@Name = '&name']]", 1, uri);
Returns the number of login-objects for the person. Using your screenshot, should be 3 in your case.
You should check the number of logins in the auth-domain, too:
logins_in_domain = metadata_getnobj("omsobj:Login?Login[Domain/AuthenticationDomain[@Name = '&auth']]", 1, uri);
Hi @andreas_lds ,
I ran your debug lines within the macro and put them into the log:
uri_login=omsobj:Login?Login[AssociatedIdentity/Person[@Name = 'TESTUSER7']][Domain/AuthenticationDomain[@Name = 'SPDSAuth']]
num_users=-4
logins_in_domain=1
As you said, the expected value for "logins_in_domain" should equal "1", which it does. But the value of "num_users" indicates, that "n is out of range." according to the SAS documentation .
Does this translate to "TESTUSR7" cannot be found in the person objects of the metadata tree or is the syntax
Login?Login[AssociatedIdentity/Person[@Name = '&name']]
maybe skewed?
Strange. Looks as if TESTUSER7 is not the exact string in the name filed of a person-object.
Hi @andreas_lds : your code is correct!
It turned out, that I had forgotten to explicitly code the metadata connection information, i.e.:
options metaserver=<FQDN>
metaport=<Metaport number>
metaprotocol='bridge'
metauser='sasadm@saspw'
metapass='<encoded passwort>'
metarepository='Foundation'
metaconnect='NONE'
;
Therefore, it threw "rc = -3".
Thank's a lot for your help @SASKiwi , @andreas_lds
@FK21 - I'm curious to know if @andreas_lds 's version runs any faster than your version. I suspect it probably does as there is a lot less looping.
Hi @SASKiwi :
I ran my, as well as @andreas_lds 'code a couple of times (10 times each) in our production as well as our test environment.
Here are the average run times (standard deviation in paranthesis):
Average run times | FK21 | andreas_lds |
TEST | 2.68 (0.08) | 2.62 (0.45) |
PROD | 22.89 (0.39) | 23.39 (0.5) |
@FK21 - what's the size difference of the repository PROD versus TEST? A much larger PROD repository might explain some of the difference in processing time.
Save $250 on SAS Innovate and get a free advance copy of the new SAS For Dummies book! Use the code "SASforDummies" to register. Don't miss out, May 6-9, in Orlando, Florida.
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.