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

Versions aren't created when owned relations are updated #195

Closed
22 of 24 tasks
ScopeyNZ opened this issue Nov 30, 2018 · 61 comments
Closed
22 of 24 tasks

Versions aren't created when owned relations are updated #195

ScopeyNZ opened this issue Nov 30, 2018 · 61 comments

Comments

@ScopeyNZ
Copy link
Contributor

ScopeyNZ commented Nov 30, 2018

Description

When writing a relation that is owned by a versioned DataObject, a new version is not created. There's a few ways this can be observed and I've written a test that shows this (#196).

Steps to reproduce

  • Have a versioned dataobject that "owns" some relation
  • Update the relation (and write)
  • Note that the parent dataobject has no new version

Acceptance Criteria

  • Allows to list all changes between two versions of an owner (incl. direct and indirect owned relations)
  • Allows to list unpublished changes relative to current owner version (incl. direct and indirect owned relations)
  • Allows to determine if an owner is modified if any (direct or indirect) owned relations are modified
  • Allows to determine if intermediate owners are modified
  • Does not mark siblings and unrelated graph nodes as modified
  • Allows to determine modification status after changes have been rolled back
  • Does not mark owners as modified if an owned relation has been archived
  • Does mark owners as modified if an owned relation has been unpublished
  • Each entry uniquely points to a version of the original record
  • Groups snapshot items for each original action which triggered the snapshot
  • Tracks author and date on each group
  • Supports deep nested ownership graphs (5+ levels)
  • Supports propagating changes to multiple owners
  • Supports all owner relationships: has_one, has_many, many_many, many_many_through
  • Supports "intermediate" owners which aren't versioned (in order to reach actual owners which are versions)
  • Supports moving of owned relations to different owners without falsifying past "snapshots"
  • Does not significantly slow down save operations
  • Does not rely on creating new versions on owners
  • Is resilient to infinite loops on potentially recursive owner structures
  • Is resilient to partial data sets (can't retroactively create this data)
  • Tracks changes in subsites and fluent languages (and can filter by them)
  • It's obvious to developers reading up about versioned that this option exists (as an opt-in for now)

Subtasks

  • Get buy-in on overall implementation from one or more bespoke teams
  • Test on a project with a large number of DataObjects (> 50) and dense version histories (> 100,000 rows)
  • Resolve rollback bug (cannot currently roll back owned changes that are strictly draft, i.e. no publish event in between)
  • Investigate viabilty of overriding versioned-admin versus augmenting it (e.g. with injectable graphql queries)
  • Implement snapshot-admin graphql/react UI
    • Query to merge versions and snapshots
    • Pagination
    • Sorting
    • Suppress redundant snapshot/version overlaps (i.e. on a publish event)
    • Snapshot detail view
      • Activity feed
      • Rollback
      • Preview

Out of scope subtasks: (separate story)

  • Compare two snapshot previews
  • See changes between two snapshots

Notes

  • Out of scope: Rollback behaviour, draft preview and archive view (continue to rely on existing _versions metadata)
  • Out of scope: Tracking many_many_extraFields (should use many_many_through instead)
  • Should not be used to compute rollback states or provide the basis for any state modifications
  • Does not need to support campaigns directly, beyond providing "modified" indications on the records contained in a campaign
  • Relies on cascading publish on owned relations to "reset" change status, does not track this separately
  • Solving the definition of a "tightly coupled ownership" (e.g. owned + cascade_deletes) is out of scope
  • Sort modifications are out of scope, see No clear way to handle sorting with versioned object #180

Related

Related PRs

@robbieaverill
Copy link
Contributor

It'd be great to get feedback on this problem from the @silverstripe/core-team.

Another examples of impact from fixing this "bug": a page owns a has_one featured image. You edit the title of the image in asset-admin, the previously published version of the image and the page that owns it both go into a modified state, instead of just the image.

It's important to note that next time you publish the page, you'd get the new version of the image with it anyway, so this is really an intermediary to highlight that there are draft changes in the page that you may otherwise be unaware of.

One flow on effect from implementing a fix for this in the context of silverstripe-elemental is that every time you make a change to a nested content block for a page, you'd get a new version of the page. This is probably desired behaviour, but you'll quickly end up with a tonne of page versions.

@maxime-rainville
Copy link
Contributor

I guess it depends how tight the ownership relationship is. You'll have cases where the owned object is non-sensical without its owner (e.g.: elemental area and content blocks). You'll have other cases where the owner object can still be helpful without the owner (e.g.: a hero images that gets attached to a page).

Off the top of my head, my preference would be to keep the existing behavior and maybe add an extra super ownership relationship model on top.

@ScopeyNZ
Copy link
Contributor Author

ScopeyNZ commented Dec 2, 2018

Like some sort of private static $inherit_modifications = [ 'ElementalArea' ];?

@maxime-rainville
Copy link
Contributor

Perhaps ... or maybe some options on the owns relations. e.g.:

private static $owns = [
    'ElementalArea' => [
        'tightlyCoupled' => true
    ],
    'HeroImage'
];

It might make sense to have that flow to the UI as well. e.g.: Prevent CMS users from directly publishing a content block and force them to always go through the parent page.

@ScopeyNZ
Copy link
Contributor Author

ScopeyNZ commented Dec 2, 2018

Hmmmn. Interesting point. /cc @newleeland @clarkepaul . It doesn't really make sense to individually publish blocks (etc) if changes to these relations make a draft version of the parent page. Will probably need some UX advice here.

Maybe it only makes sense to create a version of the page if:

  • For HasOne: the ID of the related record is changed
  • For HasMany: The set of IDs related to the parent is changed (additions/removals)

This would probably have to "flow through" for elemental though. An element that is added to an area should probably create a draft version of the page the elemental area belongs to.

And the order of IDs probably should matter as well. When you re-order a HasMany relation I'd expect a new version of the page.

@sminnee
Copy link
Member

sminnee commented Dec 3, 2018

Hey, I just want to flag that there's a significant risk that we wind up with an inconsistent system if we attempt to fix this without understanding the previous decisions made during the development of this during the original 4.x build.

Having @tractorcow weigh in on this will be helpful. For the rest of us, we'll want to dig through previous ticket history. I'll see if I can pick up some useful links.

@sminnee
Copy link
Member

sminnee commented Dec 3, 2018

Ok so these two tickets go into some discussion about the meaning of owns and its relationship to cascade_deletes

@sminnee
Copy link
Member

sminnee commented Dec 3, 2018

Notably: owns + cascade_deletes is the pre-existing mechanism developed to indicate "tightly coupled" objects.

@tractorcow
Copy link
Contributor

tractorcow commented Dec 3, 2018

I think that automatically writing the "parent" object whenever we write a child object certainly has some risks: Namely, massive saturation of the versioned table potentially increasing the ability of users to track versions. More data !== better data.

The mechanism we use internally to "segment" versions, where no specific version ID may exist for a point in time for a record, is actually "archiveDate", which is where we represent a point in time as the slice rather than the parent version number.

The problem is that we only do this for all items in the tree AFTER the top level. I.e. get a version of a record by its ID, but the versions of owned objects by date.

My suggestion is to look at allowing users to revert to specific points in time, including the top object version, rather than version IDs. The "select a version" interface becomes "select a date" interface (perhaps in addition to?).

Another possible suggestion is that when we save a record in the CMS, we create a new version of that record, and bypass the "only if changes exist" check in DataObject::write(). This check should be retained as core default, but only bypassed if the specific save is triggered by a user interaction. That way there is an immediate user feedback between "I pressed save" and "I can see a new version in the history".

What I would highly caution against is "create a new owner version every time an owned version is modified".

@sminnee
Copy link
Member

sminnee commented Dec 3, 2018

My suggestion is to look at allowing users to revert to specific points in time, rather than version IDs.

The alternative is to only roll back whole changesets, which was one of the reasons why we opted to package all publication events into changesets.

@tractorcow
Copy link
Contributor

tractorcow commented Dec 3, 2018

The alternative is to only roll back whole changesets, which was one of the reasons why we opted to package all publication events into changesets.

Yes, even better. :) However, a change may be in the wrong changeset (i.e. write of a different page to the one you want to roll back).

@sminnee
Copy link
Member

sminnee commented Dec 3, 2018

What I would highly caution against is "create a new owner version every time an owned version is modified".

I believe that the approach we had anticipated was that showing a "modified on draft" icon might interrogate the recursively owned objects. Do you have any recollection about this @tractorcow

I could foresee performance issues with that, but I would be inclined to create separate denormalised datasets to speed this up, rather than dumping responsibility for tracking this on the Versioned field.

@sminnee
Copy link
Member

sminnee commented Dec 3, 2018

So I would class this ticket not so much as a bug but as an API design decision that @ScopeyNZ believes is causing problems.

Could we clarify exactly what user features that this is causing issues with, perhaps using specific examples eg from elemental?

I get the feeling that it has something to do with the "modified" status in the tree view, and maybe something about the rollback feature, but that's a bit of a guess tbh

I think it will be useful to reframe those things as the acceptance criteria of this card and then work out what (non-breaking) changes to our API we want to make to address this.

@tractorcow
Copy link
Contributor

I believe that the approach we had anticipated was that showing a "modified on draft" icon might interrogate the recursively owned objects. Do you have any recollection about this @tractorcow

Yes, I just think that would be possibly slower, but not impossible to do. I would try to avoid doing this on many items in a list, which probably is where we would get the benefit of such a function.

I have to run off and come back to this ticket later by the way. Just a drive by opinion dump at the moment sorry. :)

@ScopeyNZ
Copy link
Contributor Author

ScopeyNZ commented Dec 3, 2018

Could we clarify exactly what user features that this is causing issues with, perhaps using specific examples eg from elemental?

Yeah without getting caught up in the technical I think I can give a good example of the weird UX that you can achieve as it is currently using elemental as an example:

  • Create a blocks page. Publish it. Check the history - 2 versions, saved and published.
  • Add a bunch of blocks with some content and save/publish these all individually
  • Check the publically visible version of the page - all your changes are shown ( ✅)
  • Open the history for a page - the latest live version is the same as before all the blocks - and the summary of the version (and the preview) shows no blocks
  • To demonstrate this potential issue further; delete a block that you just added
  • You have no history of that block you just deleted visible within the CMS now.

I get the feeling that it has something to do with the "modified" status in the tree view, and maybe something about the rollback feature, but that's a bit of a guess tbh

I didn't consider the idea of the "modified" status in the tree view actually. Currently the rollbackRecursive actually works really well because it does it based off of time (as I understand) and meets A/Cs of restoring correct sort orders (etc).

The problem is not every action is actually captured in a version. Although ordering is still a tough problem in it's own right (refer to the "context" section of this issue and the further down comment with more examples)

I feel like adding versions when relations are specifically changed to different objects wouldn't cause a huge amount of versions - right now the lack of versions is a problem. My comment before I think makes sense:

Maybe it only makes sense to create a version of the page if:

  • For HasOne: the ID of the related record is changed
  • For HasMany: The set of IDs related to the parent is changed (additions/removals)
    This would probably have to "flow through" for elemental though. An element that is added to an area should probably create a draft version of the page the elemental area belongs to.

And the order of IDs probably should matter as well. When you re-order a HasMany relation I'd expect a new version of the page.

@sminnee
Copy link
Member

sminnee commented Dec 3, 2018

OK that's a bit clearer, thanks

Open the history for a page - the latest live version is the same as before all the blocks - and the summary of the version (and the preview) shows no blocks

So, this seems like the core issues. Let's start with some assertions:

  • As of SS4 we made the design decision to create a changeset for every publication event. We did this because we predicted/hoped that we might get into these issues, compared to having 2 contradictory publication models.
  • We could therefore use changesets, rather than record versions, to show a history of publications (but not a history of changes to draft :-/)

A couple of questions then is:

  • Can we link a bunch of changesets to the records that they impact?
  • Can we get away with just showing publications, our does our history view need to show draft changes?
  • If the answer is "no" to either of those, can we link a bunch of nested-record-changes to the object(s) that own them?

Merely nudging the version numbers seems like it might be insufficient inasmuch as we need to link the particular version of the owner/parent record to the particular versions to the owned/child records. Does our data model have support for this?

Changesets at least have a good model for grouping a block of related changes (like different files in a git commit) together. But, to date, we only use them for publications, and using them for draft changes is likely to have significant performance implications.

@sminnee
Copy link
Member

sminnee commented Dec 3, 2018

I feel like adding versions when relations are specifically changed to different objects wouldn't cause a huge amount of versions - right now the lack of versions is a problem.

It's not about the number of versions, it's about whether you're breaking an assumption on which the entire rest of the application depends.

@sminnee
Copy link
Member

sminnee commented Dec 3, 2018

In general, it seems like we have a few broad approaches for addressing this:

  • Recursively tickle the version number of every owner object when an versioned object is written (Guy's solution)
  • Recursively traverse the owned objects to look for changes, whenever building a history list.
  • A middle approach:
    • When a versioned object is written, a related-change record is written linking the recursive owner objects to this.
    • When a history list is built, source it from both direct changes and related-change records

The 3rd approach is similar to the 1st option, but introducing a new place to write it rather than overloading the meaning of Version. It's also similar to a pre-emptively written caching layer for the 2nd option.

@ScopeyNZ
Copy link
Contributor Author

ScopeyNZ commented Dec 3, 2018

I fear that going option 2 will fall into the same trap we've been experiencing very recently on a certain key project - where code like this doesn't perform well on larger sites.

I'm not entirely sure on the differences on the first and third approach because I'm admittedly still a bit unfamiliar with the intricacies here. I'll do some delving and form an opinion over this week.

@maxime-rainville
Copy link
Contributor

Probably worth pointing out that ChangeSet and ChangeSetItem are designed with the idea that they could be reverted after the fact ... but we didn't quite get there.

Right now, when you publish recursively, a ChangeSet is created with matching ChangeSetItems for each affected object. All of these are saved to the DB and pretty much become useless afterwards. Basically, there's no way for the user to interact with that change set data. Even if there was, there's nothing useful you can do with them right now.

@sminnee
Copy link
Member

sminnee commented Dec 3, 2018

Yeah, making improvements in keeping with the current api design would be nice if possible ;-)

@robbieaverill
Copy link
Contributor

robbieaverill commented Dec 3, 2018

If we were to go with a change set approach, how would that affect devs with simple ORM relationships? Very basic example from my comment earlier: a blog post has one featured image. You modify some text field on the image and save, the page doesn't go into draft state even though it will change when you publish it (in so much as its featured image). Note that the dev hasn't even seen the name "ChangeSet" during this process, so it'd need to be wrapped up under the hood.

I kind of see what you mean in terms of where you're making the change - you haven't modified the page at all, so why would you bump a version for it, but I think it's quite important to think of this holistically as content changes in general. Elemental is a good example where a page's data is mostly all stored in nested ORM relationships on the page rather than fields attached to it which are covered by the page's versioning by default. The more you do that the more and more useless versioning becomes for pages (the heart of the CMS).

When I was thinking about it the other day I had a suspicion that a new API like @maxime-rainville suggested at the top would be a safer option. It'd kind of be like cascade saving but in the opposite direction to cascade publishing at the moment.

@chillu
Copy link
Member

chillu commented Jan 10, 2019

Had a good whiteboard session with @ScopeyNZ. I think we should try to stick with the date-based preview, archive and rollback behaviour, and not try to achieve full relational integrity on versions of an ownership graph. This is assuming that while there might be some bugs in the current date-based rollback behaviour (e.g. around deleted records), they can be fixed and aren't structural flaws.

But in order to allow for fast aggregate change tracking, I think we need to record the path from the originating change up the ownership graph, excluding siblings. I can't see a scenario where we can allow for fast aggregation without this level of denormalisation. Hence the need for a VersionSnapshots table.

I'm suggesting a VersionSnapshot table with the following structure:

  • UUID (Varchar)
  • Created (Datetime)
  • ObjectClass (String)
  • ObjectID (Int)
  • ObjectVersion (Int)
  • IsPublished (Bool)

Every save and/or publish operation to an object in the ownership graph would follow up the chain of its owners, and add an entry for itself and each of its owners. This operation would be wrapped in a transaction, and receive a UUID.

Then you can use the following queries to identify the modification state of the whole object graph:

  • $lastPublishDate = SELECT Created FROM TestObject_Live WHERE ObjectClass = 'TestObject' AND ObjectID = 5 ORDER BY Created DESC LIMIT 1
  • $uuids = SELECT UUID FROM VersionSnapshot WHERE ObjectClass = 'TestObject' AND ObjectID = 5 AND Created > $lastPublishDate
  • $isModified = SELECT COUNT(*) FROM VersionSnapshot WHERE ObjectClass = 'TestObject' AND ObjectID = 5 AND UUID IN ($uuids) AND IsPublished = 0 ORDER BY Created DESC GROUP BY ObjectClass, ObjectID

There's probably a way to do this in less than three queries, but it's a hell of a lot more efficient than traversing the whole object graph and comparing PageMaxVersion etc. A similar approach can be used to roll up all changes in an object graph for an activity feed. But it would not be used for rollbacks, since it only contains partial data of the object graph.

Scenarios

In the following scenarios, we're assuming ownership relationships between records of type A, B and C. I've staggered the version numbers for clarity.

Scenario 1: Bump page versions

Step 1: C1 is saved (not published), creating a new draft version v31. This creates new versions up the ownership chain, but not to siblings (B1 and A1, not B2). B1 and A1 are marked as modified due to this.

image

Step 2: C1 is published, creating new published version v32, and new versions up the ownership chain. B1 and A1 are no longer marked as modified.

image

Scenario 2: Version Snapshots

Step 1: C1 is saved, creating a new draft version v31. No other versions are created up the ownership chain. B1 and A1 are marked as modified due to the VersionSnapshot data being written below, not because their own version has increased.

image

New VersionSnapshot entries are created for each record

UUID Object Version IsPublished Created
def123 C1 v31 0 12:30:00
def123 B1 v20 1 12:30:00
def123 A1 v10 1 12:30:00

Step 2: C2 (a separate child under a sibling) is published (not saved). No new version of C1 is created.

image

New VersionSnapshot entries are created for each record, one set for the "save", and one for the "publish" (existing SilverStripe behaviour). C1 is still unpublished, meaning B1 and A1 are still marked as modified.

UUID Object Version IsPublished Created
def123 C1 v31 0 12:30:00
def123 B1 v20 1 12:30:00
def123 A1 v10 1 12:30:00
abc456 C2 v51 0 12:40:00
abc456 B2 v20 1 12:40:00
abc456 A1 v10 1 12:40:00
fff678 C2 v52 1 12:40:01
fff678 B2 v20 1 12:40:01
fff678 A1 v10 1 12:40:01

With the query examples above, we can effectively ask "give me all the snapshots where A1 is involved after it has last been published". Then "give me the latest versions of each object involved in those snapshots, and check if any of them haven't been published". I think that deletions would be treated the same way as modifications (marked as IsPublished=1).

I'm further suggesting that we try to build this VersionSnapshot behaviour as a module, rather than committing to this in core while there's so many unknowns.

The tradeoff here is that concurrent writes involving the same object graph path by different authors could end up causing incorrect modification status information in that chain, e.g. if the same object is saved by one author, and published by another author, in the same second. My feeling is that's rare enough to be acceptable, particularly since it's not exactly data corruption (_versioned table stay as-is), but rather inaccurate auxiliary info that's not used to modify any data. The only usage of VersionSnapshot tables is to efficiently roll up changes in order to mark owners as modified, and provide an activity feed.

@sminnee
Copy link
Member

sminnee commented Jan 10, 2019

I'm suggesting a VersionSnapshot table with the following structure

A few things:

  • I think it would be good to have a table where there was 1 record per UUID. So a pair of tables, say, VersionSnapshot and VersionSnapshotItem. It just seems like it would be a more coherent data-model.

  • You can use BIN(16) to store a UUID in binary, which I think would be valuable for performance, for something used as an index. http://php.net/manual/en/function.bin2hex.php might help with presenting them in a friendly way; if you wanted to get fancy you could do a custom DBField. Or, if you had a separate VersionSnapshot / VersionSnapshotItem table, you could just use a regular silverstripe PK (which has been suggested we shift to allowing for UUIDs in another ticket).

  • We'd probably also want to clarify the relationship between these snapshots and changesets, but maybe that can wait until this behaviour hits core. In general, I would see a changeset as less granular data structure, and so keeping both "snapshots" and "changesets" seems viable, but perhaps "changesets" could be refactored to reference the snapshots that they are destined to publish. Use git as an analogy, snapshot = commit and changeset = branch.

  • I'm still holding a candle for the VersionMin / VersionMax approach, but it can be a subsequent PR or something ;-)

Scenario 1 Step 2: C1 is published, creating new published version v32, and new versions up the ownership chain. B1 and A1 are no longer marked as modified.

My understanding is that publication shouldn't create a new version, but rather merely copy the existing version to the Live stage?

I'm further suggesting that we try to build this VersionSnapshot behaviour as a module, rather than committing to this in core while there's so many unknowns.

I've got mixed feelings about this, but I'd be okay if we did that planning from the get-go that this would be a temporary module and, say, in 4.5 we'd expect to refactor the code into core. I think that retaining this as a module in the longer-term would make it harder to use and maintain.


But TL;DR – go forth and build! 🎉

This was referenced Jan 10, 2019
@chillu
Copy link
Member

chillu commented Jan 11, 2019

VersionSnapshot and VersionSnapshotItem table separation is fine, it has the added benefit of tracking separate creation dates for the entire snapshot, rather than multi-second windows for different objects.

Regarding UUIDs, I'm starting to wonder if that's overcooking it. You're just as likely to create table locks on concurrent writes as with the existing SiteTree table structures. In other words, if you're writing versioned objects, it's quite likely writing to SiteTree_* somewhere. Now it's also writing to VersionSnapshot. Maybe we'll keep the UUID aspects to the broader discussion at silverstripe/silverstripe-framework#8411.

Thinking about VersionSnapshot a bit more, I think we also need an deleted boolean. Actually, the full surface available on versioned tables: WasPublished, WasDraft, WasDeleted. If we ignore many-many relationships (and stick to many-many-through), I believe this would allow us to model the complete state of an ownership graph at any point in time. I've done a bit of data modeling based on the examples above in actual SQL, and it looks promising: https://gist.github.com/chillu/f98f75fc98d461dfe23574f5e6686198

Two nested sub selects aren't ideal, but even if we don't find a way to flatten this into joins, it's a max of three queries per item you want to check (e.g. when listing a page tree), as opposed to ~2x the amount of queries as you have owned object connections in the graph.

If I'm right and we can model the whole ownership graph, we could in theory use this for rollbacks. I can't see us using it for previews, since they need to preview un-owned versioned objects as well. Given we have already implemented a date-based rollback on ownership graphs which models what the preview does, I'm not sure how much value there is here.

Another comment on Sam's Element.PageVersionMin idea: Each object can have multiple owners, even owners of the same type. I don't see how we can normalise that into the database row of the owned object itself. We have discussed a tighter ownership ($owns + $cascade_deletes), but even that isn't an exclusive ownership. From my understanding, the system doesn't throw an exception of you configure this on two different types of owners for the same owned object.

@chillu
Copy link
Member

chillu commented Jan 31, 2019

I've added some ACs, can you all please check my understanding? @unclecheese has actually been battle testing my approach for a few days, has written out scenarios as "SQL unit tests", and performance testing with 1m+ rows. Looking good so far.

Does not mark owners as modified if an owned relation has been deleted from draft or live

Is this right? In my mind, even with cascade_delete, that would only unpublish or delete owned relationships, but it doesn't delete a owned relationship from live if it was previously deleted from draft. So since there's no way for a publication of the owner to change the deletion status of an owned relationship, it shouldn't show up as modified.

Is resilient to partial data sets (can't retroactively create this data)

We could create a version snapshot for the current ownership graph on any changed owned object, but there's no way to recreate this for historical versions. If we don't make a migration task for this, you'll have websites which inconsistently mark records as changed (nor not) based on the last time they've received a draft change in their ownership structure. This seems acceptable as a first cut, but we might be forced to write this task eventually.

@dnsl48
Copy link
Contributor

dnsl48 commented Jun 5, 2019

Criterion "Does not significantly slow down save operations"

Performance testing was done with the following setup.

Page -> Block -> Image

One page has many blocks, each one of the blocks owns the image.
As such the image has multiple owners.

Here's some testing results

Without snapshots:

  • Page save: 2s
  • Create a new block: 5s
  • Save the image: 3s (1 owner) -> ?? (20 owners) -> 13s (40 owners) -> 18s (60 owners)

Snapshots installed:

  • Page save: 5s (40 blocks) -> 3s (60 blocks)
  • Page publish: 20s (40 blocks) -> 16s (60 blocks)
  • Create a new block: 8s
  • Save the image: 4s (1 owner) -> 16s (20 owners) -> 28s (40 owners) -> 44s (60 owners)

Notes

Without snapshots; Save the image: 3s (with 1 owner) -> 13s (with 40 owners) -> 18s (60 owners)

This may be indicating the tests aren't only including snapshots delta, but also other factors as well.

Result

Save operation (for the image asset). Owners are blocks owning to the image.

number of owners no snapshots with snapshots
1 owner 3s 4s
20 owners ?? 16s
40 owners 13s 28s
60 owners 18s 44s

@chillu
Copy link
Member

chillu commented Jun 6, 2019

Criterion "Does not significantly slow down save operations"

OK, I'd say that's good enough. The "image has 40 owners" case is a bit of an edge case anyway, so the fact that save operations on this already fairly complex codebase slows down from 13s to 28s isn't great, but acceptable for an alpha-level module. I think @unclecheese took the optimisations as far as he could, the next step would be to calculate snapshots asynchronously, which would then cause all kinds of fun edge cases - but might be a necessary evil for more complex projects. Let's get a bit more feedback in the wild before sinking more time into it.

It's obvious to developers reading up about versioned that this option exists (as an opt-in for now)

We still need to add some docs to versioned, right? It should point out the current limitations to developers, and then point to this module as an early stage approach.

@ScopeyNZ
Copy link
Contributor Author

For anyone looking back on this issue - this has been closed with the introduction of new experimental modules that relate versions to one-another.

See the docs: https://docs.silverstripe.org/en/4/developer_guides/model/versioning/
Or the base module: https://github.com/silverstripe/silverstripe-versioned-snapshots

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests