Marcus wrote:
Otis wrote:
The processing is thus required outside the RDBMS? I asked this because if the processing can be done INSIDE the RDBMS, the routine would be perhaps even more efficient and a stored procedure would be, in this case, a better choice.
Well actually my question regarding the fastest way was more about minimising the .NET overhead on post processing the query results in LLBLGen. Ideally I'd like to have the results in the most "raw" state. The routine in question exists in a mutlitple instance Service (different machines) consumed by both the web application and external clients and has a potential to be hit more than 200+ times per second at peak times. To this end, in LLBLGen, which method of fetching does the least work?
a dynamic list fetch will be a good candidate, or a proc call. Both will fill a datatable with raw data.
You can though also try to formulate the things you want with scalar queries, which effectively will offer you the processing on the server (don't know if they're useful in your situation)
Otis wrote:
How did you set this up, as I think caching is perfectly possible: the ACL's for the resources are static and the ACL's for the user can be loaded when the user hits the webserver for the first time. You then combine the two to get the access level of the user for a given resource. Or are both changing a lot?
Yes, I can see where you're coming from... However, how do you deal with stale data in one of the web farm's server's cache? Roll on SQL 2005 and database cache invalidation (that is of course... if it actually works in real applications, not just demos
)
Well, sqlserver 2005 is so far away that I wouldn't consider it for a live app now
How's the load balancer set up btw? One server per user, or does it switch to a random server for every request? If it's one server per user, you can utilize that in your app of course.
When a user authenticates, the list of groups (groups support inheritance) that the user is a member of is enunerated and cached for the lifetime of the request. A list of resources that will be required to process the request is determined and a single fetch is performed on the ACL table getting all required permission rows. Resources also support partial permission inheritance, so this query is dynamically created to ensure a single fetch is perfomed as an optimisation. The resulting rows are processed, merged and assigned to their appropriate resource. These are then also cached for the duration of the request.
ACLs are not static, they do change... albeit infrequently. The issue with caching at higher levels is that unless the cache invalidation is synchronised (and I have a plan for that too, below) then you will have instances where one server's cache has fetched fresh data and others still have stale data. The result is that the user has differing permissions on each page request until the caches get back in sync.
How about this: you create a table for cache control. Whenever ACL's are changed, you set a flag in there and have a timestamp in there as well (flag setting, can be something else, as long as the timestamp changes when ACLs are updated). For every request, in the global.asax code behind, you fire a scalar query on that system table. If the timestamp is different from the timestamp of the cached version of the table, the ACL's also cached on that server, are out-of-sync, and you refetch them. This thus takes a very simple query which is fired for every request, and a bigger query which is fired when ACL's change.
You can then use the cached ACL's in the app, the global.asax code takes care of the maintenance, and if you add a trigger to the ACL table, you have no maintenance and no extra code in the ACL maintenance code.
I did have the idea to invalidate the cache at regular well know intervals, say every 5 minutes. Therefore new permissions would only become "live" on a five minute boundary 09:05, 09:10 etc... But then the load on the server is going to shoot through the roof on these intervals which is not ideal. So then I thought about calculating a hashcode for the ID of the User which would act as an offset for the invalidation timeout. So for userID= 1234 the hashcode would be for example "34" so I simply invalidate the cache for this user on the 5 minute boundary + the hashcode value of 34, resulting in 09:05:34, 09:10:34 etc... Using this method all servers can calculate when to invalidate a particular user's cache entries and everyone keeps in sync...
Also a possibility, though can still be slower. That is: if the main ACL read queries are slow. The best optimization starts with measuring what's the real bottleneck
(like: if in-core processing of sets of ACL's is slower than fetching the data, it's better to optimize the ACL processing than to try to optimize the query reads.)