-
Notifications
You must be signed in to change notification settings - Fork 156
API speed-up (ETag, Last-Modified) #95
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| <?xml version="1.0" encoding="UTF-8" ?> | ||
| <database> | ||
| <name>*dbname*</name> | ||
| <create>true</create> | ||
| <charset>utf8</charset> | ||
| <table> | ||
| <name>*dbprefix*notes_meta</name> | ||
| <declaration> | ||
| <field> | ||
| <name>id</name> | ||
| <type>integer</type> | ||
| <notnull>true</notnull> | ||
| <autoincrement>true</autoincrement> | ||
| </field> | ||
| <field> | ||
| <name>file_id</name> | ||
| <type>integer</type> | ||
| <notnull>true</notnull> | ||
| </field> | ||
| <field> | ||
| <name>user_id</name> | ||
| <type>text</type> | ||
| <notnull>true</notnull> | ||
| <length>64</length> | ||
| </field> | ||
| <field> | ||
| <name>last_update</name> | ||
| <type>integer</type> | ||
| <notnull>true</notnull> | ||
| </field> | ||
| <field> | ||
| <name>etag</name> | ||
| <type>text</type> | ||
| <notnull>true</notnull> | ||
| <length>32</length> | ||
| </field> | ||
| <index> | ||
| <name>notes_meta_file_id_index</name> | ||
| <field> | ||
| <name>file_id</name> | ||
| </field> | ||
| </index> | ||
| <index> | ||
| <name>notes_meta_user_id_index</name> | ||
| <field> | ||
| <name>user_id</name> | ||
| </field> | ||
| </index> | ||
| <index> | ||
| <name>notes_meta_file_id_user_id_index</name> | ||
| <unique>true</unique> | ||
| <field> | ||
| <name>file_id</name> | ||
| <name>user_id</name> | ||
| </field> | ||
| </index> | ||
| </declaration> | ||
| </table> | ||
| </database> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| <?php | ||
| /** | ||
| * Nextcloud - Notes | ||
| * | ||
| * This file is licensed under the Affero General Public License version 3 or | ||
| * later. See the COPYING file. | ||
| */ | ||
|
|
||
| namespace OCA\Notes\Db; | ||
|
|
||
| use OCP\AppFramework\Db\Entity; | ||
|
|
||
| class Meta extends Entity { | ||
|
|
||
| public $userId; | ||
| public $fileId; | ||
| public $lastUpdate; | ||
| public $etag; | ||
|
|
||
| /** | ||
| * @param Note $note | ||
| * @return static | ||
| */ | ||
| public static function fromNote(Note $note, $userId) { | ||
| $meta = new static(); | ||
| $meta->setUserId($userId); | ||
| $meta->setFileId($note->getId()); | ||
| $meta->setLastUpdate(time()); | ||
| $meta->setEtag($note->getEtag()); | ||
| return $meta; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| <?php | ||
| /** | ||
| * Nextcloud - Notes | ||
| * | ||
| * This file is licensed under the Affero General Public License version 3 or | ||
| * later. See the COPYING file. | ||
| */ | ||
|
|
||
| namespace OCA\Notes\Db; | ||
|
|
||
| use OCP\IDBConnection; | ||
| use OCP\AppFramework\Db\Mapper; | ||
|
|
||
| class MetaMapper extends Mapper { | ||
|
|
||
| public function __construct(IDBConnection $db) { | ||
| parent::__construct($db, 'notes_meta'); | ||
| } | ||
|
|
||
| public function getAll($userId) { | ||
| $sql = 'SELECT * FROM `*PREFIX*notes_meta` WHERE user_id=?'; | ||
| return $this->findEntities($sql, [$userId]); | ||
| } | ||
|
|
||
| public function get($userId, $fileId) { | ||
| $sql = 'SELECT * FROM `*PREFIX*notes_meta` WHERE user_id=? AND file_id=?'; | ||
| return $this->findEntity($sql, [$userId, $fileId]); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| <?php | ||
| /** | ||
| * Nextcloud - Notes | ||
| * | ||
| * This file is licensed under the Affero General Public License version 3 or | ||
| * later. See the COPYING file. | ||
| */ | ||
|
|
||
| namespace OCA\Notes\Service; | ||
|
|
||
| use OCA\Notes\Db\Note; | ||
| use OCA\Notes\Db\Meta; | ||
| use OCA\Notes\Db\MetaMapper; | ||
|
|
||
| /** | ||
| * Class MetaService | ||
| * | ||
| * @package OCA\Notes\Service | ||
| */ | ||
| class MetaService { | ||
|
|
||
| private $metaMapper; | ||
|
|
||
| public function __construct(MetaMapper $metaMapper) { | ||
| $this->metaMapper = $metaMapper; | ||
| } | ||
|
|
||
| public function updateAll($userId, Array $notes) { | ||
| $metas = $this->metaMapper->getAll($userId); | ||
| $metas = $this->getIndexedArray($metas, 'fileId'); | ||
| $notes = $this->getIndexedArray($notes, 'id'); | ||
| foreach($metas as $id=>$meta) { | ||
| if(!array_key_exists($id, $notes)) { | ||
| // DELETE obsolete notes | ||
| $this->metaMapper->delete($meta); | ||
| unset($metas[$id]); | ||
| } | ||
| } | ||
| foreach($notes as $id=>$note) { | ||
| if(!array_key_exists($id, $metas)) { | ||
| // INSERT new notes | ||
| $metas[$note->getId()] = $this->create($userId, $note); | ||
| } elseif($note->getEtag()!==$metas[$id]->getEtag()) { | ||
| // UPDATE changed notes | ||
| $meta = $metas[$id]; | ||
| $this->updateIfNeeded($meta, $note); | ||
| } | ||
| } | ||
| return $metas; | ||
| } | ||
|
|
||
| private function getIndexedArray(array $data, $property) { | ||
| $property = ucfirst($property); | ||
| $getter = 'get'.$property; | ||
| $result = array(); | ||
| foreach($data as $entity) { | ||
| $result[$entity->$getter()] = $entity; | ||
| } | ||
| return $result; | ||
| } | ||
|
|
||
| private function updateIfNeeded(&$meta, $note) { | ||
| if($note->getEtag()!==$meta->getEtag()) { | ||
| $meta->setEtag($note->getEtag()); | ||
| $meta->setLastUpdate(time()); | ||
| $this->metaMapper->update($meta); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to store this data in the database? It seems to me like this data is directly bound to the files and it should be possible to get this directly from the file objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice, if there would be a way to do this. But I didn't find one (see #51 (comment) for some use cases which have to be considered). If you have any idea on how to improve this, please give feedback. But also after the discussion with @BernhardPosselt, I don't see another way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So
last-modifiedinside the response header andmtimeof the actual note (file) are independent values, right? The header value will be stored in the database and the mtime is directly set on the note.But would it hurt to generate the ETag for every response instead of caching it in the database?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, mtime of the file is set by the client ("application-level" change time) and last-modified by the server (more technical level)
This is already the case. The ETag is used to detect if the file has been changed. But the goal is to update "last-modified" of that file if it has changed. Only for this, we need the database.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But what is the ETag field in the database used for then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is used to detect whether a note was changed (newly calculated ETag of a note differs from the ETag in the database) or not (newly calculated ETag is equal to the one in the database). In the first case,
last-modifiedis updated tonow. Based on this, the note will be fully transferred to all clients on their next request, even if they usepruneBeforeto reduce the size of the response.Please note, that this ETag is not equal to the file based ETag of nextcloud/server since it includes all attributes of a note, e.g. favorite tag (and the ETag in the response header of a list response is a completely different one).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any news on this? Can it be merged, now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with this.