-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactoring layers and cache #3175
Conversation
There could be less rows to project after filtering.
(mapv project-application-overview) | ||
(filter-with-search query)))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can change the order of these operations like in the next function (slightly improve perf)
(applications/delete-application! (:application/id event)) | ||
(applications/delete-application-and-reload-cache! (:application/id event)))))) | ||
(application/delete-application! (:application/id event)) | ||
#_(if (= "expirer-bot" (:event/actor event)) ; will reload in the end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be removed. Cache invalidation (and reload) happens automatically.
(->> (mapcat applications/get-my-applications user-ids) | ||
(->> (mapcat application/get-full-personalized-applications-by-user user-ids) | ||
(mapv :application/id) | ||
distinct | ||
(mapv applications/get-application) | ||
(mapv application/get-full-internal-application) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have to get the full internal twice (the previous get-my-applications
returned overview?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(->> (mapcat application/get-full-personalized-applications-by-user user-ids)
(distinct-by :application/id))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it need to be a full internal application?
Consider implementing get-full-internal-applications-by-user
#_(log/info "Start indexing" (count app-ids) "applications...") | ||
(doseq [app-id app-ids | ||
:let [application (get-full-internal-application app-id)] | ||
:when application] | ||
(index-application! writer application)) | ||
#_(log/info "Finished indexing" (count app-ids) "applications"))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove commented code
(locking index-lock | ||
(when-some [requests (seq @indexing-requests)] | ||
(let [events (mapcat first requests) | ||
get-full-internal-application (second (first requests))] ; NB: doesn't matter which get-full-internal-application is actually used | ||
(index-events! events | ||
get-full-internal-application)) ; TODO avoid by moving to system ns | ||
(reset! indexing-requests nil)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Locking not necessary with
reset-vals!
get-full-internal-application
could be static reference if we move the system ns
(locking index-lock | ||
(swap! indexing-requests conj [events get-full-internal-application]) | ||
nil)) ; process manager expects sequence of new events) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Locking not necessary (see above)
- sequence of new commands?
@@ -0,0 +1,76 @@ | |||
(ns rems.cache |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Implement (use from service.cache)
;; - Schema in use | ||
;; - response codes | ||
;; - user info from headers | ||
;; - access rights |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Who is what role? Coarse-grained access control, i.e. who can call what API?
;; - transforms between db and internal representation | ||
;; - plain Clojure data | ||
;; - Schema supported | ||
;; - rems.service |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fine-grained role-based access control
;; - access rights | ||
;; - rems.cache (NEW) | ||
;; - collection of caching functionality | ||
;; - centralized access to cache status, invalidation and statistics |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Config
;; rems.api -> rems.custom -> rems.cache -> rems.service -> rems.db -> pg | ||
;; - cache complete joined representation | ||
;; - items are customized later in code so everything cached can be shared | ||
;; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OPTION 4
- rems.api -> rems.service -> rems.service.cache (rems.cache) -> rems.db (rems.cache) -> pg
- rems.service.cache joins stuff together and caches full representation
- rems.db can use low level rems.cache to cache db data
- rems.watcher to notify when something has changed, listeners will invalidate and/or recompute
- rems.service.cache may cache customized / personalized content and collections if necessary
- additional projections for user-roles, user-applications etc. performance critical data
;;; Cache | ||
|
||
(defn reload-cache! [] | ||
;; TODO implement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove?
:application.event/voted) | ||
user-applications ; no user added | ||
|
||
:application.event/copied-from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this actually be copied-to
if we want to add the new application id to the members of the (old) application?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{:event/type :application.event/copied-from
:application/id new-app-id
:application/copied-from (select-keys application [:application/id :application/external-id])}
So the app-id
is the new application id. The members come from application
which is from the command, i.e. the old application before this event (and with members).
(reduce (fn [user-applications userid] | ||
(update user-applications userid app-id)) | ||
user-applications | ||
(:application/members application)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
; NB: applicant gets the copied application already from `created` event.
:application.event/reviewer-joined | ||
(update user-applications actor conj-set app-id)))) | ||
|
||
(defn- user-roles-projection [user-roles event application] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this name be user-application-roles-projection
or so because these are only the roles that come from applications. Not workflow handlers, roles table etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider also just listening to application
and updating based on its :application/user-roles
data that is enriched.
:application.event/applicant-changed | ||
(-> user-roles | ||
(update (:application/applicant application) disj-set :applicant) | ||
(update (:application/applicant event) (conj-set :applicant))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should make the old applicant a member?
(defn get-users-with-role [role] | ||
(set/union | ||
(users/get-users-with-role role) | ||
(applications/get-users-with-role role))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should consider workflow handlers. Currently applications/get-users-with-role
does not do it but could.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There isn't a test that requires it at the moment. This is only user to enrich super users into applications which could use the "roles" table directly. We can remove this function.
(set/union (roles/get-roles (getx-user-id)) | ||
(organizations/get-all-organization-roles (getx-user-id)) | ||
(workflow/get-all-workflow-roles (getx-user-id)) | ||
(applications/get-all-application-roles (getx-user-id)))) | ||
(application/get-all-application-roles (getx-user-id)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's refactor this to use rems.service.user/get-roles
which would use rems.service.cache/get-roles
and do this whole thing. User roles would be cached as a projection but the whole result should be cached too (it joins many tables).
(when (empty? (:errors result)) ; nothing can't change when there's an error | ||
(dependencies/notify-watchers! {:application/id (mapv :application/id (:events result))})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this double notification? Wouldn't the lower level functions be enough (add-event
etc.)?
(defn get-applications-with-user [userid] | ||
(throw (RuntimeException. "get-applications-with-user not implemented"))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed?
We can close this one as we have merged #3320 with new ideas. |
Goals:
get-full-internal-application
instead of always justget-application
)rems.service.cache
to cache "joined things". E.g. fully populated application with users, workflow, form etc. joined to the map.rems.cache
for basic caching things and use it throughout.NB:
Open issues:
rems.system.xxx
?Related issues:
Checklist for author
Remove items that aren't applicable, check items that are done.
Reviewability
Backwards compatibility
Documentation
Testing
Follow-up