I have a laravel app that is being used by multiple users at once, they all have the possibility to edit some projects in our system.
What I dont want is to have 2 people at the same time editing the same form.
If that happens I would like to tell other users that the current form is locked and is being edited by username.
If user then leaves or submits the form, then the form is opened for other users to work with.
I have checked out both optimistic and pessimistic locking, I don't think this is correct for me
What I was thinking of doing, was to create a unique identifier in the table, that is being edited, and if the user on the page has the identifier, I would lock all other out, but how would I know if the user closes the browser window? then all other users still can't access the page/form?
How would you guys suggest I go about this?
Add a nullable locked_to column to your projects database. Then in the Project model add this field to $dates array so it can be converted to Carbon instances automatically.
When someone opens the project for edit just set locked_to field to future date, i think +5 seconds may be a good choice. Edit form should send ajax request every 5 seconds to keep project locked for next 5 seconds.
When user saves project changes, locking will be stopped cause no ajax requests will be send. In that case you also have field which may tell you when the project was opened for the last time.
User won't be able to edit project if locked_to field is equal or greater than new Carbon instance.
UPDATE - more info
There is no need to clear this field. If user didn't finish editing - he just reloaded page, navigated to other page, closed window or browser, died etc... ajax for locking won't be executed anymore, so in the next 5 second currently edited project will be unlocked - locked_to field will contain earlier date than current.
If the user keeps the page open put dead man switch prompt "are you still there", no user input will cause the page to stop the 5 second ajax refresh, thus allowing new users access.
If user returns the next day and clicks Yes, simply refresh the page, check if someone else doesn't already have it locked in and alert, alerting accordingly, but if not, restart the ajax 5 second timer.
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I am currently working on a application with a edit button:
My edit button:
<th title="Edit task" class="edit" style="float: right; $color;">
<?php
echo "<a href=edit.php?id=" . $row["id"] . ">
<i class=material-icons>edit</i>
</a>";
?>
</th>
My goal is that only one person can edit a article at a time. Is this even possible? If it is how can I do it?
If you only want to disable button to avoid concurrency editing then it is a terrible idea because users always can open the edit page by direct link. In fact, you need to implement concurrency control.
You can implement locking of record to concurrency control.
For example, you can use the following simple algorithm for the implementation of concurrency control:
You add param that will contain an end time of locking to article
On the edit page: you compare this time with current time if locking time greater then current time then you inform a user about the impossibility of article modified, else you show edit page to the user
On the edit page: if you show edit page of an article to the user then you increase end time of locking param for example in increments of 5 minutes
If the user finishes a record modification and saves it then you should reset end date of locking
But instead of this algorithm, you can implement optimistic locking.
Optimistic locking provides users open an edit page at the same time, but it forbids to save record in a parallel way. It is a better way to avoid troubles of parallel modification of a record
Optimistic locking consists in acceptance or rejection сhange of record to depend upon record version.
It works following: each article has a 'version' param that contains number of record modification. One user opens the edit page of an article that has version equal to 1 and another user opens it at the same time. Both of them save the same article. At first, you get one request for record modification and compare version of a stored record with version of an updated record if they equal one another then you should accept change and save an updated record in a storage and increase 'version' param in increments of 1 otherwise, you should reject change. As a result of this algorithm, the stored record will have version equal 2. Next, you get the second request and compare versions again, but since version of a stored record is equal 2 and version of an updated record from second request equal 1 this change is rejected. Of course, you must inform the user about the rejection of its change and give the user an ability to update the new variant of record
This is what I have done in my application:
When a user presses edit button, log the timestamp in database and redirect user to edit page. In edit page set a timer and show it to user that can edit record until timer runs out.
For example based on your record you can consider 2 minutes for user to edit a record. Time will start from 01':59'' and reach to 00':00''.
For other users when click on edit button, you should check if another user is editing that record and you can do this by calculating time from the log in database and now.
If user edits record under 2 minutes and saves it, you can remove edit log to let others users to edit.
it's not completely possible as of my point of view.
but try to do with below way.
you could add two columns in your database 'open' and 'time'
Set 'open' to 'true' or 'false' if someone is editing it. Also set a timestamp to the 'time' column. Replace the timestamp every so many seconds while editing takes place.
If someone else opens it, check the 'open' column and of it's 'true', calculate the time passed from the 'time' column. If it's over a certain time (say 2 or 3 minutes), assume the other user isn't editing anymore and allow this user to edit.
To make things clear,
you need to add one ajax call which is update your timestamp after every min/sec.
if you are in edit page.
so if someone close the browser then timestamp will not update and after next user is tried to edit then if timestamp is older then 2/3 min then allow to edit that user. beacuse we assume that user is close the tab or browser so flag is not updated but timestamp is older.
this is not perfect solution but you can try it.
There is the easy way and the hard way.
Easy way: Check concurrency on save with a timestamp.
You can simply add a timestamp (usually named update_at) in your database along with your data.
You put the timestamp in the page (or a $_SESSION variable which is safer) so that when you POST your changes you also post the timestamp. On the server side you verrify if the timestamp in your database is the same as the one you posted and if it's not you return a message saying that the post was editted.
If you care about what the user was typing (and most of the time you should) you can always display both edit side-by-side and allow him to choose/edit one of them.
Hard way: Store lock timestamp in your database
You can add a timestamp with your data the same way as in the easy way (but this time we'll call it last_edit_time).
When a user enters on the edit page you do this:
You check if the last_edit_time is 30 seconds away from the current time
If it's been less than 30 seconds you return a message saying the data is being editted
If it's greater than 30 seconds you set the value of your last_edit_time to the current time and show your edit page
Once the user is on the edit page you start a javascript interval of 15 seconds
Every 15 seconds you send an asynchronous request (Ajax) to the server telling it to update the last_edit_time to the current time. We send it every 15 seconds so that if the asynch call takes a long time the user comming to edit right on the 30th second doesn't load the page.
Doing this will assure you that only one user can access the page at the same time. If you want to make it even more secure you would add the user Id next to your last_edit_time to verrify on the moment of saving if it is the same user.
You can even set the last_edit_time to null once the user is done editting. This will unlock the data for everyone as soon as possible.
Of course there can be some edge cases where two users opens the page in the same second. To fix this you can always increase the precision of your timestamp (milliseconds instead of seconds).
You can do this very easily Please follow these step.
1)Create one table name as editLog where you column
id,member_id/user_id,is_editable(default 1)
2)Now in initial stage table is empty first user came to edit fire AJAX when user click on edit at the same time check from the database if any data is not exiting in the table then allow him to make the changes(and when you save or update the data in table check with member id also to ensure member are same) and if data exit then give him the error of warning..
Follow these step if you still have confusion comment.
The table from which you are getting $row["id"] add one more column in it and name that column as ipAddress with default value of null.
Now when user go to "/edit.php?id=$id" update the ipAddress column of table with user ip. You can easily get the user ip address by JS or by Server variables.
After that in edit.php you can easily check if current ip is equal to the ipAddress like:
if($_SERVER['REMOTE_ADDR'] == $ipAddress){
// Allow edit
}
else {
// Donot allow edit
}
And after edit is done or browser close you can set ipAddress to null.
I did a little research and I found out that it is really hard to get this done... What you can do is run a check if there is a recent update on the article (same as pending edits on SO)
According to my understanding, the solution to your problem is very simple.
First, make a table or tables in which you will record the update queries made by users in your own way
Make a script which will execute the oldest query of the table which you made in the first step and then delete that query on success mean after updation is run successfully
Learn Cron Jobs
Create a cron job for that script for a certain interval of time
And enjoy your life :)
i want to create a web page with data, this data can be edited in Real Time.
Users will see data in Real Time and can edit it, something like "Google Sheets" where everyone can edit the same file and see others changes in real time.
I will be using PHP, MYSQL, AngularJS.
I want to consult you on how to do it in the best way, this is some points that i thought of:
Use angular polling every X seconds to update page data in real time, but if user editing one of the fields, how can i prevent from this specific field to be updated by polling?
How can i LOCK specific field that user is editing, to prevent 2 users to edit the same field in real time
There is any better way to pull data in real time than angular polling?
When user editing text field i want to update it in database without "submit" or save button, i thought to save the data after 3 seconds, any better ideas?
Thank you,
1:
I'd suggest for you to have an array of objects or a datastructure similar to that, which contains the fields, in your AngularJS controller. When a user starts editing a certain field, you could set isEditing to true in the field object in your datastructure. Whenever an update comes in, you loop through your datastructure and only update fields of which isEditing isn't set / isn't true.
For making it more realtime, instead of polling, setup WebSockets and let the server broadcast the newest values of a field to all editors whenever it gets changed.
2:
For locking a field that a certain user is working on, you could add a locked column to the database table containing the fields. Whenever a user wants to start editing, the following would occur:
User requests to edit a field
Server checks if the field is locked
If the field isn't locked, the user is permitted to edit the field and the server sets the locked column to true or to the username, depending on your needs.
If the field is locked, the user isn't permitted to edit the field
When a user saves a field after editing it, you should set the locked column to false. You should probably also set the locked column to false whenever the editing user gets disconnected.
3:
PHP on it's own is not able to send data to the client without the client making a request. You'd need to add WebSocket support to PHP (for example http://socketo.me/, haven't tried that out though).
If you are interested in a server platform that is able to do this out of the box, you could take a look at http://nodejs.org. When you plan on using Node.js, I suggest using http://socket.io/ for maximum browser compatibility. (it includes fallbacks for whenever WebSockets aren't supported by the users browser)
4:
You could save the current value every x seconds if the value is different from the previous save. This would be more efficient than always saving the value. You'd need to save the previously saved value in a variable for this.
My page is visited by multiple users at the same time.
User 1: visits the page and changes the name of title
User 2: user 2 was already on that page but sees the old title, the title automatically has to be updated to new title.
I know i can simply use AJAX to call every 5 minutes, but im trying to see if there is any other way possible that fires an event to all instances of the page opened by different users that if one of them is updated all other pages get automatically updated with latest data without the wait of 5 minute ajax call. Ajax seems inefficient since it will do many ajax calls and also what happens if user 1 updates title while user 2 updates title as well before user 2's page has been updated with 5 minute ajax call.
Not asking for a code, just need an advise whether I should keep using AJAX calls every 5 minutes and be happy with it or there is a better solution.
Try investigating web sockets for real time, two way communication between server and browser.
http://socketo.me/
I'm in the early stages of working with it myself but it seems like a solution that would fit your requirements.
Also, maybe look at push notifications
e.g. http://www.pubnub.com/blog/php-push-api-walkthrough
I'm creating a PHP Web application, which would involve:
1) Users opening a record
2) Users making changes to the record
3) Saving changes to the record
Since this is a multi-user application, I want to prevent situations where two users have the same record open at the same time, and one user's changes overwrites the next, preferrably by enforcing some sort of locking method when a record is opened that automatically unlocks when the user navigates away from the page.
By record, you mean SQL records? If so, you could add another column isOpen. Set it to 1 as long as someone else has it open, and in that case, do not serve it to anyone else.
In situations like this, it works best to also implement a timeout mechanism, where a record can be open only for 'x' min before being forcibly closed.
(Edit: This answer is assuming you want to keep a record locked the entire duration a user is viewing the info fetched from the table. If you want to lock a record only for the instant that a read/write operation is occuring on that record, MySQL engines have inbuilt mechanisms for that)
In response to your comment
To make a record accessible to others when the active user navigates away, off the top of my head, I can think of two ways to achieve it:
Allow the timeout mechanism to take care of it. Depending on your scenario, a short enough time window could work fine.
In addition to the timeout, also implement a heartbeat mechanism - an Ajax script on the page polls the server letting it know the page is still open. If the user navigates away, the server recognizes the skipped heartbeat, and unsets the record. In this case, the timeout would still take precedence. So, if the user leaves the window open and walks away, the server would still receive the heartbeat, but when the time window closes, the server unsets the record (despite still receiving heartbeats).
I use a field update_date. When user reads the record I write a cookie with this date. When user updates the record and submits the new data I'am adding WHERE update_date = '$my_escaped_date' AND id = '$the_edited_id' and if mysql_affected_rows is zero I'm showing error message that the edited data is old. It's not perfect as if you edit old data you must reenter it, but it does the job.
A locking method is exactly what is available in mysql:
http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
It's not automatic but it allows you to lock a table, do stuff and then unlock it again.
Be carefull tho' that the system does not become locked up if you forget to unlock a table or a user takes a long time to change something and you only unlock it when that user submits the form.
A better way might be to read data from the table and upon submission of the form, check to see if the data has not been altered. If it has you can notify the user of the changes and other wise you can lock the table, perform the changes and unlock it again.
You can add a field in_use to the records table,
when a user open that record update its value to 1 and when he saves it
update it back to 0.
If the value is 1 - the record is locked and won't be opened for other users.
I'm working with some existing PHP/MySQL code. I'm logging/tracking certain activities into a MySQL database. Certain access points are being logged. The number of times a user logs-into the system is counted. But, I need to also log the amount of time a user is logged-in, as well as the time the user is in a certain section of the Web site.
Since PHP is a stateless environment, for the most part, what's the best way to record the end-point(s); or when the user logs-out?
Note: I can't force the user to log out, as the browser can just be closed. Maybe I could just put up an AJAX timer that would count the minutes? If so, should I treat activities and time logged-in as different tables of information (MySQL)?
Edit: I forgot to mention we do have jQuery available.
Like you said, you can't force the user to logout, and you can't know for sure whether he's looking at your page or playing Pinball.
A solution would be an AJAX request every, say 5 minutes, to tell your application that the user is active. Unfortunately, if your user has locked his screen and went to play Pinball, you still don't know exactly what he is doing. Also, doing AJAX requests at intervals like this will increase server load, especially in a multi-user environment.
The best solution I think is to simply store the start_time of the user (when he logs in), then to update the end_time at every action he does, and with a session timeout.
Per example:
I log in at 5:00. Update the start_time to 5:00.
I browse to foo.php at 5:01. Update the end_time to 5:01.
I browse to bar.php at 5:03. Update the end_time to 5:03.
I go for a coffee at 5:05.
I come back at 5:15 and my session expired, I need to relogin.
So, you know I spent roughly 3 minutes on your application, since the last action I did was at 5:03 (5:03 - 5:00 = 3). Of course, you can't know exactly if it was 3 or 5 minutes. But you can assume, most of the times anyway, that if I don't do anything on your application (i.e.: execute a script, call, etc.), that I'm not using it.
Obviously, if you can capture JavaScript events like window close it's even better, or if I sign out manually: you update the end_time accordingly.
You need to capture two events.
The onCLose() event for the page and hook that into an ajax call back to your logging system.
The onClick() event for your logout button and hook it into the save ajax handler.
The onClose event will allow you to capture when either the tab/broswer is closed and the onCLick event is obvious.
Now this will not capture times when the browser dies, the machine loses power etc. so there will be instances where you will have gaps and those can be corrected by your login event handler and simply tag the last login event as logout out on the next login. This will however lead to outliers in your tracking of time spent logged in and you will need to statistically deal with those in your reporting.
You can use an extra PHP script that records the last activity and call it via ajax.
You can use javascript to monitor if the user is still active (moved mouse or pressed a key in the last 5 minutes etc.)
EDIT: Almost forgot the important part: your java script must make an ajax request eery X seconds.
So if there was no request in x+tollerance seconds you can consider the session as dead.