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

OCC scanner commit in batches #27527

Merged
merged 2 commits into from
Apr 18, 2017
Merged

OCC scanner commit in batches #27527

merged 2 commits into from
Apr 18, 2017

Conversation

PVince81
Copy link
Contributor

Description

Autocommit after 10k entries when running occ scanner to avoid excessive memory usage.

Related Issue

Fixes #27516

Motivation and Context

How Has This Been Tested?

Manually

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

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.

@jvillafanez @butonic

also see #27516 (comment) for potential concerns.

@PVince81 PVince81 added this to the 10.0 milestone Mar 28, 2017
@mention-bot
Copy link

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

@PVince81
Copy link
Contributor Author

also @mmattel might be interested too

@mmattel
Copy link
Contributor

mmattel commented Mar 28, 2017

Idea, can also be done in a second step, but would like to see it together:
There are usually 2 triggers, quantity and time.
Proposal, have a second trigger to do the DB commit based on time. Lets say every 4-5 seconds. Either one of the two will fire first.
Beneft:
over fast wires, the commit will be triggered by quantity,
over slow wires, the commit will be triggered by time.

@mmattel
Copy link
Contributor

mmattel commented Mar 28, 2017

Maybe helps to fix: #25029
The root cause was never identified. But the quantities in the issue "smell" that it could be related.

@@ -45,6 +45,8 @@
* @package OC\Files\Utils
*/
class Scanner extends PublicEmitter {
const MAX_ENTRIES_TO_COMMIT = 10000;
Copy link
Member

Choose a reason for hiding this comment

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

Has it been tested with a low memory limit, let's say 255MB or less?. It looks too big to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

tried locally without this patch with 47k files, did not run into the memory issue unfortunately.

In fact, in all of my tests I never ran into a memory issue. I'll redo one one of these days as scanning 100k files can take many many hours...

Copy link
Contributor

Choose a reason for hiding this comment

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

When you run the test, would you be able to check the memory consumption and share the result?
The consumption should show a pattern like a sawtooth.
Such a result would be eg informative in the documenation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mmattel what tools do you recommend to make sure measurement ? Or just log memory usage to the ownCloud log ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Imho, owncloud log and the use of php memory consumption methods would be sufficient.
We need to get a guideline.
Currently we are flying blind...

@@ -69,6 +85,7 @@ public function __construct($user, $db, ILogger $logger) {
$this->logger = $logger;
$this->user = $user;
$this->db = $db;
$this->useTransaction = !(\OC::$server->getLockingProvider() instanceof DBLockingProvider);
Copy link
Member

Choose a reason for hiding this comment

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

This might need a comment. It looks weird that you're using transactions when you aren't using DB locking.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this was there before I just changed the way the state is stored.

no idea why this condition exists... might need some git archeology

$this->triggerPropagator($storage, $internalPath);
$this->entriesToCommit++;
if ($this->useTransaction) {
$propagator = $storage->getPropagator();
Copy link
Member

Choose a reason for hiding this comment

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

you can move this inside the if. No need to get a propagator if it isn't going to be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

you're right, also no need to increase entriesToCommit if we don't use it.
Will move both deeper.

@@ -236,5 +252,20 @@ public function scan($dir = '') {
private function triggerPropagator(IStorage $storage, $internalPath) {
$storage->getPropagator()->propagateChange($internalPath, time());
}

private function postProcessEntry(IStorage $storage, $internalPath) {
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we can make the function easier. The overall approach is to open a transaction, scan files and finally commit the transaction. The problem I see with the function is that it doesn't follow the same approach, but instead do a scan (in fact, propagate the change), commit a transaction and open a new one, which makes the process difficult to follow.

Maybe a conditional open transaction, scan and finally conditional commit is easier to follow. The only thing to be careful about should be the opening transaction and the last commit, which aren't conditional.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

can you elaborate on what you mean with "conditional open transaction" ? I don't really follow.

Note that one thing we do not want to do is commit at the end of a folder because said folder could contain more than 10k entries or so.

Copy link
Member

Choose a reason for hiding this comment

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

The function would something along these lines (I don't expect the db->inTransaction works, but I think there is something similar)

if ($this->entriesToCommit === 0 && !$this->db->inTransaction) {
  $this->db->beginTransaction();
}
// do whatever you need
if ($this->entriesToCommit >= self::MAX_ENTRIES_TO_COMMIT && $this->db->inTransaction) {
  $this->db->commit();
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the problem with your proposed change is that we also need to include the propagator's transaction, which somehow doesn't fit with the $this->db->inTransaction() approach. While both are supposed to be in sync, I find that having this specific condition using inTransaction more confusion.

Additionally we also need to check $this->useTransaction in the if statements to find out whether we should at all use transactions.

Copy link
Member

Choose a reason for hiding this comment

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

How about removing the the first and last "transactions" (the first beginTransaction and the last commit) and let it being handled by the postProcessEntry function completely?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

why?

Copy link
Member

Choose a reason for hiding this comment

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

The main reason is readablity. The "natural" way of doing things is open -> do something -> close. The current way is do something -> close -> open new.

The function supposes that there is a transaction in progress (if any, obviously), and that there is "something" that will close the transaction afterwards.

From my point of view, either the function doesn't know a transaction is in progress (as it was before) or it knows and can handle transactions properly. If it knows, it shouldn't depend on others to open and / or close the transaction.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

would it work if I simply add a comment "note: transaction is initiazed and closed elsewhere" to de-confuse devs ?

I don't feel it's worth spending time rewriting this bit if you balance the time needed (and retesting needed) with the small benefit of a bit less confusion. I'd rather add a comment then.

Copy link
Member

Choose a reason for hiding this comment

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

I agree that rewriting this bit could take some time to re-test, but I think it's something we should do now. I don't think that adding a comment is enough in this case.
Calling @butonic and / or @DeepDiver1975 to break the tie.

@PVince81
Copy link
Contributor Author

PVince81 commented Apr 3, 2017

@jvillafanez I've adjusted the other bits

@PVince81
Copy link
Contributor Author

PVince81 commented Apr 5, 2017

@butonic has tested this and confirmed that memory usage is reduced

@mmattel
Copy link
Contributor

mmattel commented Apr 5, 2017

Great, do you have numbers that you can share, as we discussed above?

@butonic
Copy link
Member

butonic commented Apr 6, 2017

Tested with very long file and folder names:

[...]
	File   /u2/files/4 250 char dir name B9DB5C4FEAD4BF3BD562ED01816D3AC538568B981110E70E20565C5349F2D53C56B3947A39E8198D8757BBF9A3A3D827A06D4EC6BD418C48A898588D32B98BEC50BCF3D3E53B811DF921C632B9FEDEF9D8689C4DCC269DBF35B01D0F374B57CC6EC073586868475D6A47172B7DAAE41414C273/4 250 char dir name B9DB5C4FEAD4BF3BD562ED01816D3AC538568B981110E70E20565C5349F2D53C56B3947A39E8198D8757BBF9A3A3D827A06D4EC6BD418C48A898588D32B98BEC50BCF3D3E53B811DF921C632B9FEDEF9D8689C4DCC269DBF35B01D0F374B57CC6EC073586868475D6A47172B7DAAE41414C273/4 250 char dir name B9DB5C4FEAD4BF3BD562ED01816D3AC538568B981110E70E20565C5349F2D53C56B3947A39E8198D8757BBF9A3A3D827A06D4EC6BD418C48A898588D32B98BEC50BCF3D3E53B811DF921C632B9FEDEF9D8689C4DCC269DBF35B01D0F374B57CC6EC073586868475D6A47172B7DAAE41414C273/4 250 char dir name B9DB5C4FEAD4BF3BD562ED01816D3AC538568B981110E70E20565C5349F2D53C56B3947A39E8198D8757BBF9A3A3D827A06D4EC6BD418C48A898588D32B98BEC50BCF3D3E53B811DF921C632B9FEDEF9D8689C4DCC269DBF35B01D0F374B57CC6EC073586868475D6A47172B7DAAE41414C273/4 250 char dir name B9DB5C4FEAD4BF3BD562ED01816D3AC538568B981110E70E20565C5349F2D53C56B3947A39E8198D8757BBF9A3A3D827A06D4EC6BD418C48A898588D32B98BEC50BCF3D3E53B811DF921C632B9FEDEF9D8689C4DCC269DBF35B01D0F374B57CC6EC073586868475D6A47172B7DAAE41414C273/44444 250 char name B9DB5C4FEAD4BF3BD562ED01816D3AC538568B981110E70E20565C5349F2D53C56B3947A39E8198D8757BBF9A3A3D827A06D4EC6BD418C48A898588D32B98BEC50BCF3D3E53B811DF921C632B9FEDEF9D8689C4DCC269DBF35B01D0F374B57CC6EC073586868475D6A47172B7DAAE41414C273

+---------+--------+--------------+
| Folders | Files  | Elapsed time |
+---------+--------+--------------+
| 111115  | 100003 | 00:17:06     |
+---------+--------+--------------+

Memory profile without this PR looks like this:

screenshot from 2017-04-05 13-54-06

@mmattel
Copy link
Contributor

mmattel commented Apr 6, 2017

@butonic how is the profile looking with this PR?
The delta would be interesting

@mmattel
Copy link
Contributor

mmattel commented Apr 6, 2017

Anyway, 👍

@butonic
Copy link
Member

butonic commented Apr 6, 2017

I did a test with with 9.1. I wrote a cachegrind when the first batch is commited:

screenshot from 2017-04-06 14-51-48

My top stayed at:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     ZEIT+ BEFEHL 
6369 www-data  20   0  374524  73952  19280 S  84,8  0,9   5:03.37 php occ files:scan u2 -v

25MB vs 196MB seems fine

@mmattel
Copy link
Contributor

mmattel commented Apr 6, 2017

HURRA, congrats, pls merge !

@PVince81 I propose a note in the feature list of v10!

@settermjd I propose a documentation note which is helpful for physical servers sizing.
Something like Scanning of files is processed in 10k files chunks. Based on tests, server memory usage for scanning +10k files used about 25MB of memory. Due to the chunking algorithm, server memory usage will not grow.

@PVince81
Copy link
Contributor Author

PVince81 commented Apr 6, 2017

Nah... need to first rearrange the code to make @jvillafanez and future devs who read this code happy

@butonic
Copy link
Member

butonic commented Apr 6, 2017

@mmattel 25Mb is only the amount of ram in use as seen by php-memprof. Top shows a usage of 73952kb so ~74Mb.

@mmattel
Copy link
Contributor

mmattel commented Apr 18, 2017

Any update on this?

@PVince81
Copy link
Contributor Author

I don't have time to finish this, sorry

@mmattel
Copy link
Contributor

mmattel commented Apr 18, 2017

would it work if I simply add a comment "note: transaction is initiazed and closed elsewhere" to de-confuse devs ?

Cant we merge with the suggested comment as note above and do the rest later?
The benefit of this PR is really big on low memory systems...

@PVince81
Copy link
Contributor Author

ok, here you go then

@PVince81 PVince81 merged commit 9ee487c into master Apr 18, 2017
@PVince81 PVince81 deleted the files-scan-commit-batch branch April 18, 2017 14:50
@mmattel
Copy link
Contributor

mmattel commented Apr 18, 2017

Thanks, 😄

@mmattel
Copy link
Contributor

mmattel commented Apr 18, 2017

Maybe a note at ownCloud 10.0 Features under General to be added ?
occ scanner optimized memory usage for large scans by using autocommits

@PVince81
Copy link
Contributor Author

@mmattel sounds good, can you add it ?

@mmattel
Copy link
Contributor

mmattel commented Apr 18, 2017

ok will do, was not sure if I am allowed to.

@mmattel
Copy link
Contributor

mmattel commented Apr 18, 2017

Done

@PVince81
Copy link
Contributor Author

stable9.1. #27881

@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.

Huge transaction in CLI scanner
5 participants