Notification bar on website - how to avoid multiple notifications for same user - caching

I am building a website that will notify users of certain changes to their account via a notification bar at the top of the screen (similar to the way SO notifies of new badges).
In my DB I have a table that list the events and also a flag to denote whether they have been notified of the change yet or not. The table is of the form :
UserID AccountEvent EventDescription HasBeenNotified
ABC123 1 Your price deal ends in 2 weeks FALSE
What I would like to achieve is:
Customer logs in and is shown notification (assuming one needs to
happen)
Customer clicks to acknowledge message
DB HasBeenNotified field set to TRUE (to acknowledge notification has occurred)
I have been able to achieve all this but there is a snag!
I am putting the events that need to be notified into the HttpRuntime.Cache with a 10 min expiry so I don't need to keep polling the DB.
The issue this creates for me is that a customer could potentially see multiple notifications of the same message until the cache expires, for example:
Customer logs in and app logic looks in cache and says customer ABC123 needs to be
notified of X
Customer then acknowledges message (which updates DB to set notification flag to TRUE)
Cache does not yet reflect updated DB so if customer navigates to new page the same notification message is fired (until cache expires)
As far as I know I cannot update the actual data within the Cache (can only Remove data from cache based on cache key).
Can anybody provide hints and tips of how to get around the issue of multiple notifications?

Why not burst the cache? Remove that key from the cache?
Or you can update the cache by setting it again to new HasBeenNotified value or even set it to null which you should deal with and accept as empty.
HttpRuntime.Cache["YourKey"] = "";//new object/value;

Related

How to manage store "created by" in micro-service?

I am building the inventory service, all tables keep track the owner of each record in column createdBy which store the user id.
The problem is this service does not hold the user info, so it cannot map the id to username which is required for FE to display data.
Calling user service to map the username and userid for each request does not make sense in term of decouple and performance. Because 1 request can ask for maximum 100 records. If I store the username instead of ID, there will be problem when user change their username.
Is there any better way or pattern to solve this problem?
I'd extend the info with the data needed with from the user service.
User name is a slow changing dimension so for most of the time the data is correct (i.e. "safe to cache")
Now we get to what to do when user info changes - this is, of course, a business decision. In some places it makes sense to keep the original info (for example what happens when the user is deleted - do we still want to keep the original user name (and whatever other info) that created the item). If this is not the case, you can use several strategies - you can have a daily (or whatever period) job to go and refresh the users info from the user service for all users used in the inventory, you can publish a daily summary of changes from the user service and have the inventory subscribe to that, you can publish changes as they happen and subscribe to that etc. - depending on the requirement for freshness. The technology to use depends on the strategy..
In my option what you have done so far is correct. Inventory related data should be Inventory Services' responsibility just like user related data should be User Services'.
It is FE's responsibility to fetch the relevant user details from User Service that are required to populate the UI (Remember, call backend for each user is not acceptable at all. Bulk search is more suitable).
What you can do is when you fetch inventory data from Inventory Service, you can publish a message to User Service to notify that "inventory related data was fetched for these users. So there is a possibility to fetch user related data for these users. Therefore you better cache them."
PS - I'm not an expert in microservices architecture. Please add any counter arguments if you have any.*

Restrict access to a page when event fires

i have created a booking system which synchronizes with Google Calendar every 5 minutes and also truncates and old data and fetches new one from calendar, this process takes about 2-3 seconds.
What i want to do is, when the event to fetch data from Google Calendar fires, i want to disable access to the route of the booking system for these 2-3 seconds then enable it again when the event ends, i want to do this because it truncates the old data and fetches new one, so if a person is looking at the booking system this 2-3 seconds all the book schedule times will be free and i don't want this to happen.
Is this kind of thing possible?
Thanks and regards!
Well, my suggestion is to create a new field in your users table something like : is_google_sync_active and route middleware.
Then just before you will start sync. you change that field to true and in your new middleware you may check whether your sync. is active or not, then when your sync. ends you just change the value of field again to false. So, every time when user tries to pass to your sync route he will be passing your middleware which wil handle all job.
In addition, you can redirect user to page which shows status or progress if the sync. is processing.

REDCap - Sending out automated emails after searching in the data for condition

I am migrating sql server database into REDCap. I am new to REDCap and I am still investigating the features of REDCap. I am building a survey form that will collect all the data into REDCap. Once the data is there in the REDCap, I want to send emails based on the date in the future. For example, if there are fields in the instrument as below
Email
Expiry Date
test#gmail.com
12/12/2021
I want to send automated email to that email address(test#gmail.com) on that date date (12/12/2021). Basically, it has to look at the data and send out reminders to the email address on the expiry date.
I looked at alerts and notifications. I can write the conditional logic to send the reminder upon data entry. But, here in my case, the data is already stored.
I looked at the scheduling module. Scheduling module is generating events on the calendar but not sending emails automatically.
Is there a way I can achieve this?
Which version of REDCap is your institution on? Since version 9.9.1 you can have an alert send either before or after a date field in your project. So the alert can be configured to be triggered by data import, and the time to send would be, say, 5 days before the [expiry_date].
Here is the changelog entry:
Improvement: A new send-time option has been added when setting up Automated Survey Invitations and Alerts & Notifications. When defining
when the ASI/Alert should be sent, the option “Send after a lapse of
time” has a new setting added so that, if desired, the user may set
the time lapse relative to the value of a date or datetime field in
the project. In previous versions, the time lapse setting could only
be set relative to the time in which the ASI/Alert was triggered. That
is still an option, but now users may also opt to send the ASI/Alert a
certain amount of time either before or after the date/time of a
specific field. This new setting will allow users to have greater
control with regard to setting when ASIs/Alerts will be sent without
getting too complicated in their setup, such as having to use complex
logic (with datediff, etc.).
As the changelog says, another method is to use datediff logic in the trigger, which you will need to use if you are not on v9.9.1 or later (you should also encourage your institution to upgrade since there are important security patches since then). When an alert has a datediff function in its logic, REDCap will check it every four hours (unless the frequency has been changed by your administrators). This means you can send the alert 5 days before the expiry date with this logic:
(existing logic) and datediff("today", [expiry_date], "d", true) = -5
The true parameter here returns the signed value, so that if the first date is later than the second, it will return a negative value. false returns an absolute number.
This will be true on the exact day when [expiry_date] is 5 days in the future.

How handle bot to count pageviews

I don't know how exactly handle this situation. I have a directory where I count the pageviews for each item. For authenticated users I only count as new pageview after a delay of 200 seconds between requests. For unauthenticated users I use the IP and also 200 seconds of delay.
I use a redis SETEX to verify and then the key will expire after 200 seconds. If the key doesn't exist, then insert a new page view.
Something like this
item_id:user_id (authenticated users)
item_id:ip (unauthenticated users)
Well, this works fine until a user try to increment intentionally the page views for a specific item. I have almost 3000 views for a specific item only yesterday (in last year the page has only 150 views...). So, he created some bot to visit the page with a delay to avoid my validation.
I need to register legit pageviews, but I need to avoid the type of fraud. Any idea?
As far as I know, the best way for handling bots is a way like Google Analytics.
Google Analytics works by inserting a JavaScript snippet into the
header of your website. This snippet counts a page view whenever a
visitor triggers that JavaScript, and most bots do not process
JavaScript.
You can integrate some kind of CAPTCHA in your application to limit the number of times a user can view the page within a specific amount of time.
Upon a set number of views within a given duration (say, 20 views in under 3 minutes) from the same user or IP, make them verify the CAPTCHA each subsequent time they try to view the page.
Issue a Token for every page view request. Store the token in the Cookie.
User your already available IP or USER_ID as a filtering mechanism.
After page loaded, User the token, old_token from the cookie, Operating System, Browser Name and IP / User_ID to validate the request.
Give two different timings, like 200 second expiration time and 3600 seconds of grace time, if any of the above data matches within the grace time, don't count the page view.
You can also extend this by keeping track of page views within grace time and create some methods to validate pageview request.
I usually register the Request Time together with the Request to measure the Visit Frequency and the Visiter Count per certain Time Span.
When you register all the Request that come in with with the item_id, user_id, ip and timestamp you can afterwards process the Registers by grouping them by user_id, ip and timestamp.
That way you can find out the amount of Hits per Second and identify and exclude those who clear surpass the normal Activity Pattern without loosing Data.
Often I use the Web Service Logs to generate Statistics about Visit Frequency to certain URLs on a hourly, daily or even monthly basis.

How to prevent two users to edit one row from DB

i am using Spring/Hibernate/ZK. In one tab i get object from DB for editing by user, but second user can open the same tab and the same object for editing . I want to informed second user whit message like "This object is аlready open" and hide buttons for save.Тhus second user can see current data from DB to this object but can`t edint him.Is there a way to check session for this object or another way to do that.
The other answers mostly look at the database, but if all users use the same zk application to access the database, you could keep track of opened objects in the Composer or ViewModel (depending whether you use MVC or MVVM; I'll just call it controller).
Your controller would need a static list of objects that are currently modified. If a user requests to open an object that is not in the list, everything is fine and your controller enables the fields and save button. Otherwise, those are disabled and/or you display a message.
The tricky part is clearing objects from that list. If a user presses the save button, you just remove the object from the list. But what if the user doesn't and just closes the tab or their session just times out? In this case you need a callback, or a mechanism that regularly checks whether the screen is still open.
You could achieve this by adding a zk timer to the tab that pings every now and then and updates the timestamp in your static list (so make it a map). If a new user tries to edit the object, check how old the last timestamp is. If it is old enough (i.e. the previous user saved it or abandoned the screen), allow them to edit it.
Still, you have to think about what to do if a user just keeps the screen open. How long are they allowed to keep the lock on the object? This is an issue in Microsoft Office as well. If multiple users try to open an Excel file from a network location, the first one gets to lock and the others cannot save until that user saves.
You may have additional field which indicates that column is being edited. When first user starts work, the field would be updated. The second user would query object with 'on hold' status and your code would handle this.
Other way - use Hibernate #Version field in your entity. It holds object version which is incremented after every update operation. If second user would save object after first one already saved, it would throw OptimisticLockException which you could handle in your code. More about optimistic and pesimistic locking: Chapter 5. Locking. Related discussions: Hibernate Automatic Versioning and When to use #Version and #Audited in Hibernate?
The best solution is to use Optimistic Concurrency Control with Versioning and when Hibernate throws Concurrency Update issue due to same row is being updated in two transaction then use one of below strategy
First Wins Strategy
Last Wins Strategy
Merge Conflicting Update Strategy
First Wins Strategy is not good solution as it leads to lost update and user will get frustrated that all his work is lost.
By Last Wins Strategy one of user will get error message that you are working on Stale data and start your transaction again . By this way also user can get frustrated due to fact that now again he need to restart operation from beginning but his changes will not lost.
Instead go with Merge conflicting Update Strategy, when Hibernate throws Stale object exception reload screen with new data and user will see updated result and allow him to proceed with latest data. In this user changes will not loss and user will not get error message , just his screen reloads with fresh data and he can decide whether to proceed or not .
You can take example any e-commerce site and you will get one of result of either Last Wins Strategy or Merge Conflicting Update Strategy. Two user can start to by one item but one of user will get message in last screen that item is not stock.

Resources