Skip to content
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

Central user account table arriving ..... #27165

Closed
wants to merge 10 commits into from

Conversation

DeepDiver1975
Copy link
Member

@DeepDiver1975 DeepDiver1975 commented Feb 15, 2017

Related Issue

fixes #23558

Missing

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

@DeepDiver1975 DeepDiver1975 added this to the 10.0 milestone Feb 15, 2017
@mention-bot
Copy link

@DeepDiver1975, thanks for your PR! By analyzing the history of the files in this pull request, we identified @butonic, @VicDeo and @PVince81 to be potential reviewers.

Copy link
Contributor

@PVince81 PVince81 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start!

public function changeSchema(Schema $schema, array $options) {
$prefix = $options['tablePrefix'];
$table = $schema->createTable("{$prefix}accounts");
$table->addColumn('id', 'integer', [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's go big ? bigint ?

'notnull' => false,
'length' => 255,
]);
$table->addColumn('quota', 'string', [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now seeing this, I remember that we already have a quota value in the oc_preferences table. But not as easy to retrieve as a single select on this table here.

Might need to think of migrating it.

'notnull' => false,
'length' => 32,
]);
$table->addColumn('backend', 'string', [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also add a backend_id which is the user id as seen by the backend ? unless we think it is always the same as the "user_id" column. That is, assuming that multiple backends can't provide the same user id.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not that clear but this is - as of now - the class name of the backend

@@ -94,7 +100,7 @@ public function getBackends() {
* @param \OCP\UserInterface $backend
*/
public function registerBackend($backend) {
$this->backends[] = $backend;
$this->backends[get_class($backend)] = $backend;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in the end this would only be called in the background job (or on-demand) as we won't need the backend any more for every operation since we can rely on the account table.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need the backend for login, inital setup of new accounts (auto provision mode) and import scenarios

} else {
return new User($ownerId, null);
}
return $this->userManager->get($ownerId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break with federated share owners which are not resolveable through the user manager. That's why we create a "virtual" user...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm - ok - then we need a second implementation of IUser for this case.
User requires an instance of Account and AccountMapper to be passed in ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, that would align with this idea: #25815 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later on I wonder if we should also have a "federated users backend" which would deliver such users, they'd also appear in the account table under the "federation" backend for quick lookup. (but that sounds redundant from the system address book)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something to think about - for sure ....

'notnull' => false,
'length' => 32,
]);
$table->addColumn('backend', Type::STRING, [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DeepDiver1975 Why it is a String? Shouldnt it be INT representing foreign key? Why do we allow NULL here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it connected to 0: initial?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's the php class name

Copy link
Contributor

@mrow4a mrow4a Feb 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you ensure, that if the backend is deleted, all related records are also deleted. Will we do single transaction doing that to ensure atomicity and consistency? Is backend primary key somewhere? This smells like possible inconsistency later, if some developer is not careful in the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a backend is not enabled or has been deleted the user can no longer login. Deleting a user is an explicit operation and nothing we should do 'just because the backend is no longer there'

Copy link
Contributor

@mrow4a mrow4a Feb 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wound ensure this not null, and add another state 4: backend-deleted or mark user as deleted, in a single transaction - also not sure about this. Actualy this sound quite dangerous for me, that someone can enable and disable backends and we still have a hanging relation pointing to nowhere. Not sure how to resolve it not to brake consistency

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes - nut null makes sense here - thx

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, because I assume we dont have a new table holding backends, we just use classnames to do that? Makes sense.

'length' => 64,
]);
$table->addColumn('home', Type::STRING, [
'notnull' => false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we allow null here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it connected to 0: initial?

$table->addColumn('state', Type::SMALLINT, [
'notnull' => true,
'default' => 0,
'comment' => '0: initial, 1: enabled, 2: disabled, 3: deleted'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0: initial, I start loving it. Is it connected with last_login, backend and home?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not yet fully sure if we need 0:initial ...

$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->iLike($fieldName, $qb->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%')))
->orderBy($fieldName, $orderBy ? 'ASC' : 'DESC');
Copy link
Contributor

@mrow4a mrow4a Feb 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

orderBy is quite expensive operation, do we need to do that by default? I think it makes sense only for displayname, isnt it?

Copy link
Contributor

@mrow4a mrow4a Feb 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why dont we use LIMIT on DB level? What findEntities is exactly doing? Please be aware of that it will go over all never-logged-in accounts in LDAP also, doing sequential read on the disk for pages for all of them!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Limit is on db level. And if we want pagination we need to sort on db level.
Orderby is not expensive is there is an index.
(Need to add it 😆)

Copy link
Contributor

@mrow4a mrow4a Feb 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Internaly in DB, in order to do limit, you have to finish all others. Adding OrderBy basically tells the plan generator that you need a whole picture of the table, thus whole scan (it is enforced by sort requiring full view on the input - blocking operator). LIMIT just tells the sort to stop sorting. Of course, unless you have index, found good reference https://www.percona.com/blog/2006/09/01/mysql-order-by-limit-performance-optimization/. Just wanted to ensure that we wont do anything crazy here :>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The users are expected to be sorted - this has to be done in the db - no other option available

->where($qb->expr()->iLike($fieldName, $qb->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%')))
->orderBy($fieldName, $orderBy ? 'ASC' : 'DESC');

return $this->findEntities($qb->getSQL(), $qb->getParameters(), $limit, $offset);
Copy link
Contributor

@mrow4a mrow4a Feb 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are passing here $limit. Why do we first scan whole the table and then limit that on PHP level? What findEntities is exactly doing, is it belonging to the query above or we extract logic from DB to PHP?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is on db level. See the code inside find entries

$prefix = $options['tablePrefix'];
$table = $schema->createTable("{$prefix}accounts");
$table->addColumn('id', Type::BIGINT, [
'autoincrement' => true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a primary key or foreign key somewhere? I might have a question here about user uniqueness, where backend tries to add user which already exists, and how do we distinguish between them

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto increment always forces to be the primary key

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is true

'notnull' => false,
'length' => 255,
]);
$table->addColumn('user_id', Type::STRING, [
Copy link
Contributor

@mrow4a mrow4a Feb 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a primary key or foreign key somewhere? I might have a question here about user uniqueness, where backend tries to add user which already exists, and how do we distinguish between them. I dont also understand why we have two IDs. DO we have redundant tables? Do you have any drawing showing relations between all involved tables?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see the index definition - this is unique

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have some db relations drawing of Migration feature so we can easier understand?

Copy link
Contributor

@mrow4a mrow4a Feb 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I see the whole picture of relations of this table with others, but it is easier to look at it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have some db relations drawing of Migration feature so we can easier understand?

I don't get this question. The migration feature is unrelated any db relations.

Copy link
Contributor

@mrow4a mrow4a left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I need to fully understand the relationships and redundancy between tables

@DeepDiver1975
Copy link
Member Author

I think I need to fully understand the relationships and redundancy between tables

I guess so 😉

@DeepDiver1975 DeepDiver1975 force-pushed the central-account-table branch 15 times, most recently from 9f0621a to 331d06c Compare February 21, 2017 11:04
@mrow4a
Copy link
Contributor

mrow4a commented Feb 21, 2017

@DeepDiver1975 I did not go in details in the code yet, but seems the priority in implementation of this table will be to put as much load on table scan of oc_account table, so most of queries will just ask for records of this table, and very very seldomely do the join with e.g. ldap table (this is expensive, if both tables are big, and it will be like that). (Does it replace ldap table, or is just normalisation ?)

@PVince81
Copy link
Contributor

very very seldomely do the join with e.g. ldap table

@mrow4a no, if we had that it would be nice.
No, the old code does a PHP-level "join" by connecting directly to LDAP... or in some cases memcache. But partial searches are not possible with memcache so they always hit LDAP.

With this table partial searches can be done only in a single location: the oc_account table.

@butonic
Copy link
Member

butonic commented Mar 9, 2017

Obviously, this will require use to kill a lot of code in user_ldap and make migration rock solid.

@DeepDiver1975
Copy link
Member Author

Then there is the task of keeping a user back end and the account table in sync. Which I see as an occ sync command. In addition to a sync all we also need a sync this uid, because for LDAP there may be triggers that can be used to update a user on demand. Gonicus eg allows that.

see #27205

@PVince81
Copy link
Contributor

PVince81 commented Mar 9, 2017

Something comes to mind: do we need a "lastsynctime" column ?
In LDAP we had "lastFeatureRefresh" in the preferences table which told the LDAP code to not try and refetch the user details based on that value.

Copy link
Member

@butonic butonic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried understanding the changes. Impressive list of test changes ... the avatar stuff can be discussed idependently.

$parameter = (string)$qb->createNamedParameter($uid);
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('lower_user_id', $qb->createFunction("LOWER($parameter)")));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

strtolower $uid instead of using SQL?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

php strtolower and database tolower behave slightly different - we have to choose one way .... (hmmm .... do I?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grrr - actually I'm mixing it 🤦‍♂️

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

* @return IImage|null
* @since 9.0.0
*/
public function getAvatarImage($size) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we really need the avatar image as part of the user. I'd prefer to implement it as an app. That app can store the avatar image inside its app folder, no need for our internal filesystem. It could try to fetch the avatar from gravatar based on the email address if an internal avatar is not available.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

separate discussion - from an api poc it is there - not much to change right away ...

@@ -183,18 +157,16 @@ public function setEMailAddress($mailAddress) {
* @return int
*/
public function getLastLogin() {
return $this->lastLogin;
return (int)$this->account->getLastLogin();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't the entity return the right type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes - it should ....


// FIXME: Feels like an hack - suggestions?
// FIXME: Feels like an hack - suggestions?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

foreign keys ftw ;-)

*/
public function __construct($uid, $backend, $emitter = null, IConfig $config = null,
public function __construct(Account $account, AccountMapper $mapper, $emitter = null, IConfig $config = null,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we don't really need the user database backend anymore, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the backend is accessible via the account

@DeepDiver1975 DeepDiver1975 force-pushed the central-account-table branch 9 times, most recently from e14c1f2 to 2bc42a7 Compare March 14, 2017 11:23
@DeepDiver1975 DeepDiver1975 force-pushed the central-account-table branch from 3aaaf2a to 3b088af Compare March 16, 2017 12:16
@@ -322,7 +322,7 @@ function testSearchByTag() {
$userId = $this->getUniqueId('user');
\OC::$server->getUserManager()->createUser($userId, $userId);
$this->loginAsUser($userId);
$user = new \OC\User\User($userId, null);
// $user = new \OC\User\User($userId, null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commented out ?

@DeepDiver1975 DeepDiver1975 deleted the central-account-table branch March 21, 2017 09:38
@lock
Copy link

lock bot commented Aug 3, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Aug 3, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

User account table and user locking/blacklist
5 participants