Closed (fixed)
Project:
Profile 2
Version:
7.x-1.x-dev
Component:
Code
Priority:
Critical
Category:
Task
Assigned:
Unassigned
Reporter:
Created:
26 Apr 2012 at 16:07 UTC
Updated:
31 Oct 2018 at 14:14 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #1
joachim commentedI'd really rather we didn't. Allowing multiple profiles per user is something some cases require. It's something I plan on writing a module for.
Comment #2
nagiek commentedI can't visualize the case were you would want two profiles of the same type, but OK.
Comment #3
joachim commentedIf each profile was a set of contact details such as:
- address
- phone
- options such as whether this address is the primary one
and you wanted people to have multiple addresses.
Comment #4
hazah commentedFor whatever reason, 2 profile records get created during registration. Only one of these gets fields. This happens when using a separate registration path. There is no way to fix it through the usual UI, as only one single tab shows up on the account's page. Any idea as to why this would be happening?
Comment #5
hazah commentedMy bad. After an exhaustive search I've come to believe it is the fault of the
profile2_email_syncmodule. Once disabled, the issue disappeared. This does highlight a usability problem, though. If you have a single user with more than one of the same profile type attached, you only get one single tab. This means you cannot ever edit or even delete the second record. This generally effects listing of content (usually through views, but this applies to other methods). For instance, if listing the users when related to their profile data without aggregation you would get duplication of the users. So while not a bug, I do believe it's not completely resolved in terms of usability.Comment #6
joachim commented> If you have a single user with more than one of the same profile type attached, you only get one single tab
I think in that case, both should appear in the one tab. I did start work on a Profile Multiple module that would do that, but I've not got very far...
Comment #7
hazah commentedWhere's that module? Maybe I can help?
Comment #8
hazah commentedSetting this to critical. As it stands there's absolutely no way to remove duplicates other than going strait through the database. This is an extremely dangerous situation to force inexperienced site maintainers into, especially since all they are probably interested in is maintaining a couple of user profiles.
Comment #9
nvalinski commentedGlad this is has been set to critical... I've finally stumbled across this after close to a month of dealing with users popping up twice on a list of users made with views with duplicate fields. Is there any way to figure out exactly which database entries are causing the duplicate issue? I've found most of the duplicate fields, but overall I am one of these relatively inexperienced site maintainers you speak of that would rather not have to pass down an issue such as this to a new maintainer who will only be less familiar with the subject.
Comment #10
oglok commentedHello guys!!
Any solution for this problem??
After much search, I found this issue forum!! I have the same duplication and I've spent sometime deleting profiles directly in the database... after that, my search api engine died...but with a re-indexing started working again.
Is it true, having the profile in the same Tab as the user configuration solves the problem??
Thanks!!!
Comment #11
hazah commentedI know that in my case, the Profile Sync Email module was the culprit. I hadn't figured out what it did to force their double creation, but once it was disabled the problem went away.
"Is it true, having the profile in the same Tab as the user configuration solves the problem??" I'm not sure what you mean. From what I see in the code it shouldn't really matter where the profile shows.
Comment #12
oglok commentedThanks for your answer @hazah
I have not installed the Profile Sync Email module... and I'm a bit desperated.
Would you know which part of the code makes the creation of the profile?
Thanks!!!
Comment #13
hazah commentedThe profile2 module seems to be written correctly, and it hooks into the 'user_add' form (which is used in user/register and admin/people/add) to add its entities and fields. If an existing user with the right permission visits the profile pages when it does not exist, one is automatically created for her to edit/view, and that's it.
Do you have any other module installed that handles registration enabled, or anything else that taps into the process of user creation? From what I gather so far it looks like some sort of a race condition going on.
Comment #14
oglok commentedThanks hazah!! I appreciate your help!
I am using Recruiter distribution. I think none of the modules included will have bad effect on Profile2. I installed LoginToBoggan in order to let users to register themselves automatically. But if I remember well, this duplication used to happen before.
Should I try to disable it?
Thanks!
Comment #15
hazah commentedIt doesn't hurt to try :).
Comment #16
oglok commentedhazah
I did this query in order to choose which profiles must be deleted.
DELETE FROM `profile` WHERE `pid` in (SELECT `pid`
FROM (
SELECT `pid` , COUNT( `created` ) AS num
FROM (SELECT * FROM `profile` WHERE `created` = `changed`) AS clnProfile1
GROUP BY `created`
) AS clnProfile2
WHERE num =1)
I know this is not the way of solving the problem... but I need a way of getting rid of the extra-profiles.
I would like to include it in the cron of Drupal... because the cron makes the reindexation for Search APIs. Do you know how to do this?
Thanks!
Comment #17
hazah commentedI haven't played with cron before, but I've seen a hook called
hook_cron. I would start reading up on that and how it works.Comment #18
raphael apard commentedThis can be a problem to have multiple profile for a user. Here a patch to avoid this issue. It's still allow multiple profil creation by a profile type option ("Allow multiple profiles per user").
Reviews are welcome ;).
Comment #19
raphael apard commentedComment #21
raphael apard commentednew one
Comment #22
raphael apard commentedAnother to replace profile2_create by profile2_load on user access category check.
Comment #23
adamharms commentedThanks for the patch. What should I do about deleting these extra profiles that showed up? I'm scared to go into the db
Comment #23.0
adamharms commentedUpdated issue summary.
Comment #24
kyleoliveira commentedAny movement here? I'm running into the same issue.
Comment #25
rcharamella commentedI've run into this same issue or at least a similar issue. In the database I'm seeing multiple entries for each of the fields in my profile. For instance, the first name can have up to 5 duplicates for each user. They seem to be differentiated by the field format. I see some set to plain_text others to full_html. Now all the views based on Profile produce duplicate results.
Any ideas what caused this situation? Any ideas on possible solutions?
Comment #26
mkryemadhi commented18: profile2-avoid_multiple_profile_per_user-1549224-18.patch queued for re-testing.
Comment #28
spleshkaGuys, can anyone tell me how to reproduce this bug? I really tried, but could not.
Solution in #22 doesn't solve issue at all, because profile2_create() function only generates new object, but not saving it.
Solution in #21 also seems not completed and confusing for users (because most of them don't know about profile duplications and may mess it with multiple profile types).
Anyway, I can't commit anything in Profile2 untill we get the reason of bug. So I am leaving this issue postponed untill I have a steps to reproduce an issue.
Comment #29
hazah commented@Spleshka Have you tried using Profile Sync Email module I meantioned in #11?
Comment #30
spleshkaNo, I didn't. But @oglok in #12 had the same issue without that module. That's why I wonder if someone found another reason. Also possible situation when different issues lead to the same problem...
Comment #31
hazah commentedWell, it'd be a starting point, no?
Comment #32
spleshkaYes, I think it could be. Do you have some time to digg into the problem?
Comment #33
mrdalesmith commentedCan't yet see a cause, but I have the same issue. The majority of users are fine, but I have one or two who have a profile assigned on registration and then a second assigned a few seconds later: one user has two profiles of the same kind assigned at Mon, 24 Mar 2014 12:47:03 GMT and Mon, 24 Mar 2014 12:47:05 GMT.
It suggests that a profile is being created and saved to the database but isn't then available when the user moves on to add their data to it, and so a new one is created for them?
Not sure I see the use case for allowing multiple profiles of the same kind, especially since there is no option for editing the oldest profiles: if you wanted - say - a user to have multiple addresses in a profile, wouldn't you just use add more fields?
ETA - somebody here suggested the possibility of the user double clicking a submit button?
Comment #34
gregloby commentedI have the same problem, when a user creates a Profile and double click the submit button the module produces doubled/identical Profiles for the user, when a user is really fast he can even makes a triple click and generates three identical Profiles.
Comment #35
gregloby commentedpatch #21applied prevents creation of doubled Profiles but now a double/triple click triggers Error and move user to Maintenance page with a hard info -
There in my Error log are both PHP and Profile2 errors:
It's not user friendly :) but better than letting the module creates duplicated Profiles, which in my case are automatically presented by Views.
Comment #36
mkryemadhi commentedThis is a Major issue causing us lots of headaches. When you have a website with hundreds of thousands of users and profiles, this tends to happen a lot.
It happens even when people do not double-click. It's as if profile2 doesn't realize that the user already has an existing filled in profile. When editting, that check is failing or some reason and a new duplicate profile is created.
Comment #37
spleshkaSo do you think that user may get a duplicated profile ONLY during editing of his existing profile, right?
Comment #38
mkryemadhi commentedI could be wrong but that's what we have seen on our instance.
Comment #39
spleshkaOkey, i will try to reproduce it myself. But if someone will find another steps to reproduce - you are very welcome.
Comment #40
kevinsiji commentedSteps to reproduce:
Comment #41
roball commentedI am having the same issue, without the "Profile Sync Email" module ever having installed. The "profile" DB table only had duplicates for the UID=1 user - it had 3 rows for it. All other users are correctly represented only by one row for each user.
Comment #42
joshuautley commented#1 I agree.
#4 Made no difference. I tested with it disabled and with it uninstalled. I know you put in number 5 that your comment number 4 was a mistake but I thought I'd test it anyway.
#5 We need this module so disabling is not an option. I tested the site (without patches) without this module but it made no difference.
Patch #22 alone did not resolve the issue.
Patch #21 resolves the issue but as stated in #35 the user is taken to a maintenance page and presented with an error notice. The only difference in my situation is that I only clicked it once. I've reviewed the error and the try/catch used in the patch for error handling and it seems the only reason this patch works to prevent duplicates is because the try/catch is failing and thus preventing the code to continue resulting in a a profile but no values.
#37 No, it happens on creation for us.
Comment #43
joshuautley commentedComment #44
guypaddock commentedOkay, I think I see what's happening here. For us, it's related to Profile2 pages.
Steps to Reproduce
1. Set-up a site with Profile2 and Profile2 pages. Make sure there's at least one profile type.
2. Log-in as a user who has not yet created the type of profile you ensured existed in step #1.
3. Go to the edit page for the new profile (i.e. if the profile type is main, go to "profile-main/edit").
4. In a new browser tab, open the same page as in step #3.
5. Fill out and save both the form in step #3 and the form in step #4.
Expected Results
The user should be able to save a new profile with the form from step #3 (since that one gets submitted first). When the user submits the form from step #4, they should receive an error that a profile already exists for the user.
Actual Results
The user gets assigned two profiles.
Possible Root Cause
When Profile2 Pages detects that a user does not yet have a profile, it creates a new Profile2 entity in-memory before processing the page callback. This happens for all actions -- view, edit, and delete. That means that if you have multiple copies of the edit page open and you submit them all, you're saving a new blank entity for each one.
This normally would be fine, and is consistent with a similar pattern that the Node module in core uses when creating nodes. Unfortunately, unlike in core where duplicate nodes can just simply be deleted, Profile2 has no such ability to delete profiles of the same type for the same user.
Possible Solution
The only way I can see to prevent this issue is to do a look-up for a profile of the same type for the user when saving the profile edit form. If one exists, simply present an error that the user already has a profile of the type they're creating and that they should reload the page to edit it (similar to what happens in core when two users open a node for editing and one saves before the other).
Comment #45
guypaddock commentedBTW Based on the timing of requests in access logs where this is happening, my theory is that users are double-clicking the profile save button. That, coupled with the fact that Profile2 doesn't protect against creating multiple profiles at the same time, and you get multiple profiles per user.
Think of users who click the button even more than twice if they think the request stalled...
Comment #46
guypaddock commentedHere's a patch that started with the approach from #21 and #22 but with the following key differences:
profile_create()andprofile2_create(). Technically, there's nothing inherently wrong with Profile objects being constructed in memory that happen to match an existing profile for a specific user; it's when the profiles get saved that we have an issue. Hence, the check for dupes needs to happen on save not object instantiation. (If we really do want to check on instantiation, we'd want to do that in theProfile::__construct()instead).try...catchfor flow control in thesave(). If you really are just throwing an exception to immediately catch it, just log an error. Instead, I think we really do want to throw an exception but have the caller figure out what to do with it. In this case, I moved the code that catches the exception into the form submit function for the profile form.Comment #47
guypaddock commentedTypo fix in the call to
watchdog_exception().Comment #48
guypaddock commentedSetting to NR (fingers crossed on tests).
Comment #49
guypaddock commentedDocs fix and flow correction. Let's acquire the lock sooner.
Comment #50
skylord commentedCan confirm that patch at least applies cleanly. As for now - no more duplicates appear. It needs some monitoring and more checks but currently all is OK.
Thanks for your investigation and patch!
Comment #51
guypaddock commentedGreat, thanks!
BTW if users start getting errors that a profile already exists, it's likely that they're double-clicking the profile save button. You may want to also follow this issue in that case:
#1705618: Double click prevention on form submission
Comment #52
joshuautley commentedApplied patch but I am getting the following error...
DuplicateProfileException: Duplicate entry of Member Profile (member) for user 13 in Profile->checkForDuplicateCreation() (line 1000 of /srv/bindings/9053b31136564dd99a50bbb81904b7ae/code/sites/all/modules/profile2/profile2.module).In my use case this error is created the first time I click save for the profile type (even if I have not entered any data yet). Prior to this the PID (profile ID) was 0 but then is assigned during the process. And, since there is now a PID I can enter data into the Profile type and saving it no longer generates an error.
Any thoughts?
Comment #53
joshuautley commentedLooking at the code on line 1000 from the error...
I wonder why is this throwing an error?
throw new DuplicateProfileException($this);I had already installed Profile2 Sync Email module and so each user has a profile but only the email address is filled out. Method #1 I go to edit the account and click the profile tab on the account and enter their name and additional info and click save I get this error and none of the data is saved. Method #2 So then I repeat but don't enter data click save and get the same error but on my second edit attempt I no longer get the error no matter which method I try.
Also please note that prior to any of this I have checked for the PID (profile ID) and it is empty so one would think how could there be a pre-existing profile but maybe when I enabled the Profile2 Sync module it generated profiles but not a PID?
Comment #54
guypaddock commentedThere are two levels of error handling in the patch.
One is at the API level -- the exception you're pointing to -- that gets invoked when code within the Profile2 module or some other module attempts to create a new profile for a user who already has a profile of the same type. Basically, the exception prevents the code from doing something stupid.
The other is at the UI / form level. The Profile2 form should be validating that the profile does not already exist before even getting down to the profile2_save() call (see:
profile2_form_validate_handler()).So, basically, UI level prevents an error from occurring through proper validation, while API level throws an exception to prevent bad data (kind of like an exception of last resort).
The exception is not the cause of the problem, but does signal that there's either bad profile data being added to your DB by other code, or something else strange with the way that the profile is being saved. Perhaps Rules or something else is saving the profile prematurely, before the actual profile save happens?
Comment #55
joshuautley commentedAlright, thank you for the explanation. I will do some more research to see if I can nail down the cause.
Some thoughts...
I have double checked to ensure the UI box for allowing duplicates is unchecked (default).
I enabled the Profile2 Sync module maybe too early? Could this module be running some code that is the cause? (EDIT: Disabling the Profile2 email Sync module resolved the error. I'll try to enable it once I've updated the remaining user profiles and then test creating a new user with a new profile for the first time with Profile2 Sync module enabled. Thank you Guy for the assistance.)
Why does the error occur the first time I save the profile but not the second time?
If you could offer a response to any of these thoughts/questions it would be appreciated.
Comment #56
guypaddock commentedThe reason you don't get the exception on subsequent updates is because the first time you're saving, the form is creating a new profile, while every time after that it's editing the same profile. The exception and error handling is only there to prevent creation of a second profile of the same type for the same user, not to prevent editing the existing profile.
This means that the problem you have should only be one of the following situations:
I'm leaning toward #2, since again, you'd get a validation error in the case of #1.
Comment #57
guypaddock commentedAnother case (#3) would be that something is breaking the form, causing it either not to detect the existing and not to validate properly.
Comment #58
joshuautley commentedIt was #1. So, do we start and issue for this here... https://www.drupal.org/project/issues/profile_sync_email?categories=All ?
Comment #59
guypaddock commentedHow did you confirm the issue?
Comment #60
joshuautley commentedI simply disabled the Profile2 Sync Email module then repeated the process I was doing before.
Comment #61
guypaddock commentedWell... that doesn't necessarily mean it's #1. It might be #3 in that case.
Comment #62
paean99 commentedAlthough the problems with double clicks and others variations must be corrected, i do not think that the data schema should be changed. I am of the same opinion as @joachim #1
My use case would be a site where the users can be the admin of multiples organic groups each with a different profile representing different persona.
I could use different profiles types in a situation with a limited number of groups, but not if the groups are created dynamically by the user as that would also imply for them to create dynamically the different types. What's more, in this use case, the profiles all have the same fields...
Comment #63
mrdalesmith commentedI think the major issue is that the DB structure allows for multiple profiles but the code doesn't: if you try to load a user's profile of a specific type using code, it fails to load anything if the user has two profiles of the same type. I doubt many people have a strong opinion about which needs to change, but the code and the db structure both need to be in agreement.
Comment #64
paean99 commentedIn my case i didn't use code.
I used views to select them the profiles and rules to create.
There was no problem.
But i do agree that there is a grave problem and the DB and the code are not working as a good couple.
Comment #65
kcannick commentedwhat about just throttling the creation of profiles. I doubt a person would be intentionally creating multiple profiles within 10 seconds of eachother.
Comment #66
captainack commentedI've been wrestling with this for a long time, but didn't decide to look here until I took the time to find the root cause of this (the blind saving inside Profile::save()) instead of working around it.
I was going to propose a different patch that does less enforcing - move the check for profile existence to save() similar to #49, but no locking. Simply check if the profile exists, and if so, set the pid and set it to not new.
So basically, just let the last caller win.
Then I saw #49 and thought my approach is a step backward from it. But then I realized that #49 wouldn't solve my use case. So "last caller wins" may have some benefits:
1. if something in the background touches the profile, there won't be an error displayed to the user and it will succeed
2. no lock needed
My particular problem is exactly caused by (1) above.
a) The profile is created in the registration form with profile2_attach_form().
b) In some scenarios, before the submission handler is called, a profile needs to be created with a field the user can't access set
c) Profile::save() takes the submission handler's word for it that it's a new profile and creates a dupe
I haven't tried #49 yet, but looking over the code tells me that it wouldn't help with this scenario (which means I have to keep using the workarounds I'm doing). Correct me if I'm wrong.
Another possible scenario that would have the same problem would be if the user loads a profile form and a cron job or some other hook from somewhere else creates a profile. Then when the user hits submit, they'll get the error even though they've taken time to fill out the form.
Comment #67
Khalid S commentedA simple one.
Comment #69
kevin morse commentedI have a pretty small community website with a few hundred users and one of my users just ended up with a duplicate profile. I am using no additional Profile modules so my duplicates must have been created by a user having multiple tabs open or double clicking?
Any idea how to clear out the duplicate? How does the module even know which profile to use?
The pid of the two profiles are 261 and 262. I can obviously delete one of the profiles from the 'profile' table but then what happens to all the fields associated with the profile? Do I need to go through all the field tables and delete all the rows with the corresponding entity id?
Any help would be greatly appreciated.
Comment #70
socialnicheguru commentedIs there a way to allow more of a particular type of profile.
For example I have defined a Twitter profile. I would like people to be able to add as many Twitter profiles as they want. The idea is that it would match the number of Twitter accounts they have.
Better yet could we have an admin setting per profile that sets the total number of profiles of a specific type. Much like fields do.
Comment #71
solenity commentedHere is a patch based off the patch in #67. It adds an update hook to remove duplicate profile records.
The following query will remove existing duplicate profile records, keeping the record with the lowest pid value.
Comment #72
solenity commentedComment #73
kcannick commented@Solenity Does deleting the profile record directly in the database leave orphaned field data? Example: If profile 162 and 163 are duplicates doesn't that mean there are duplicate fields created as well?
Comment #74
solenity commentedGood question Kcannick. Yes, deleting the profile record directly leaves orphaned field data. If this patch is to be committed, I think it should be updated to remove the orphaned field data.
Comment #75
solenity commentedHere is an updated patch from the one added in #71. It removes the entire profile by calling the profile2_delete_multiple method. Profiles are deleted in an update hook in batches of 50.
Comment #76
solenity commentedThe patch in #75 kept the profile with the lowest pid. This patch keeps the profile with the highest pid, which is the one that is actively used.
Comment #77
kcannick commentedPatch #75 tasted and works. Should be committed.
Comment #78
joelpittetSetting to needs review so that the testbot can test it out
Comment #79
joelpittetThis patch seems to go against the request for revisions. Not a show stopper but just wanted to make people aware. The unique key will need to be updated with a revision_id/vid for that issue.
#1043128: Profile revisions
Comment #81
joelpittetSweet 11K of duplicates removed! Thanks for the patches!
Comment #82
joelpittetFixing linter by moving return value to a variable
$existing_profile.Interdiff:
Comment #84
joelpittetAh I caught this one too late, but the short array syntax doesn't work on PHP 5.3, changed that in this patch.
Comment #85
joelpittetChanging the author to not me, but this worked so well, wanted to relay that! Thanks everybody!
Comment #86
rickj commentedI see this issue has bubbled up to the top due to recent activity, but with a long history! I'd like to close it, but I'd prefer a bit more feedback. I wasn't involved in the earlier discussions.
The latest patch #84 seems to be the most elegant solution. If we really don't want duplicates then a unique DB index is the way to go, and seems preferable to some of the earlier solutions. However, this totally precludes offering deliberate multiple profiles of the same type, either in an add-on module, or custom code. It's clear no mooted new module to support this functionality has emerged, so I suspect never will - not in D7 anyway. But can we be sure that no-one has a custom implementation? Apart from Joachim's suggestion 6 years ago, all other discussion in this issue concerns duplicate records as a bug.
Comment #76 states that if there are duplicates, the one with the highest pid is the effectively active one. Is this guaranteed? What's the logic? The install patch deletes all duplicates with lower pids, I'd like to be sure that doing this will never remove a profile record that's in use.
All help from previous contributors appreciated!
Comment #87
rickj commentedComment #88
rickj commentedI've spent more time looking at the code, and I've concluded that this patch should not cause any problems.
It would be impossible to implement duplicate profiles with a custom module because of the way the pid cache in profile2_load_by_user() works. It's also this cache that consistently picks the most recently created record (i.e. highest pid). So I'm going to commit patch #84.
Comment #90
rickj commented