<div dir="ltr">I wrote a response inline.<br><div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Apr 24, 2017 at 3:12 PM, Jeff Ortel <span dir="ltr"><<a href="mailto:jortel@redhat.com" target="_blank">jortel@redhat.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class=""><br>
<br>
On 04/21/2017 01:16 PM, Austin Macdonald wrote:<br>
> I think we have arrived at a consensus around these points:<br>
<br>
</span>+1<br>
<span class=""><br>
><br>
> * Django's ModelForm validations that are used during `full_clean` is an anti-pattern in DjangoRestFramework<br>
> and should not be used<br>
> * Validation, by our definition, will be done by the serializers in the API layer<br>
> * The Task layer should run business logic checks at the time of use<br>
><br>
> We have not arrived at a consensus on whether Updates/Deletes for Importers/Publishers will be synchronous or<br>
> asynchronous. I've tried to think through each way, and what it would need to work.<br>
><br>
</span>> *Synchronous Update and Delete:<br>
> *<br>
> _Validation Heuristic_<br>
<span class="">> The serializers validate in the API layer only, and it is desirable to have that validation occur at the time<br>
> of the write.<br>
</span>> *<br>
> *<br>
> _Concurrent Writes:_<br>
<span class="">> Let's assume synchronous Updates and Deletes. There is a possibility of concurrent writes because sync/publish<br>
> tasks write to the importer/publisher tables respectively and these tables can also be written by the Update<br>
> API at any time. If there is a collision the last write wins.<br>
><br>
> Workaround:<br>
> Concurrent writes may not be a big deal because they should be not affect each other. In platform, sync and<br>
> publish only write to the last_sync/last_publish fields, which should never be written by the update API. If<br>
> the writes are restricted to only the affected fields, then there would be no clobbering. As long as plugin<br>
> writers' implementations of sync and publish do not write to the same fields allowed in an API update, this<br>
> concurrency is safe. We would obviously need to be clearly documented for plugin writers.<br>
><br>
</span>> _Dealing With Broken Configurations_<br>
<span class="">> For example, while a sync task is WAITING, the user updates an importer in a way that breaks sync. The<br>
> importer configuration should be read only once at the start of a sync. If this configuration has been deleted<br>
> or is not syncable, business logic checks catch this and fail immediately. Even if the configuration is<br>
> changed during the sync task, the sync task will continue with the original configuration. The sync task will<br>
> update last_sync, but will not write to other fields. I don't see a problem here, as long as this is<br>
> documented for the plugin writer.<br>
><br>
</span>> _Summary_:<br>
<span class="">> I don't see any concurrency problems here. The downside of this design is that sync and publish, (implemented<br>
> by plugin writers), should not write to the same fields as API updates.<br>
><br>
</span>> *<br>
> *<br>
> *Asynchronous Update and Delete*<br>
><br>
> _Validation Heuristic_<br>
<span class="">> Assuming async updates, the data comes into the API layer where it is validated by the serializers. The update<br>
> reserves a task (reservation is based on repository) and waits to be performed. It is usually undesirable to<br>
> separate validation from use. There could be concern that data validated in the API layer could become<br>
> unacceptable by the time the Task layer actually performs the write.<br>
><br>
> Practically, this separation may not be an issue. Valid data becoming invalid involves validations that are<br>
> dependent on state. We can split these validations into two categories, uniqueness validations and multi-field<br>
> validations. Fortunately, the Django ORM will enforce uniqueness validation, so the database is protected, but<br>
> we might have to handle write failures. Multi-field validations are probably not validations by our<br>
> definition, but are business logic, which could be checked in the Task layer.<br>
</span>> _<br>
> _<br>
> _Concurrent write prevention_<br>
<span class="">> Leveraging the reserved resource system, concurrent writes could not be a problem because writes could never<br>
> be concurrent at all. Syncs, Updates, Deletes, Publishes all make reservations based on the repository (this<br>
> is an assumption based on what we do in Pulp 2), so each would run in the order that they were called.<br>
><br>
</span>> _User Controlled Order_<br>
<span class="">> A user can issue a [sync][update][sync] and be confident that the first sync will use the original importer<br>
> configuration, and the second sync will use the updated configuration.<br>
<br>
</span>This is not deterministic (from user point of view) even with task reservation.  You still have a race<br>
condition queuing tasks.  In the following example, the update made by user B will win and the update made by<br>
user A will not be used for the 2nd sync.  By making this async, seems we have done is moved the race<br>
condition from the DB to the tasking queue.<br>
<br>
queue<br>
-------<br>
1. user-A [sync]<br>
2. user-A [update]<br>
3. user-B [update]  (winner)<br>
4. user-A [sync]<br></blockquote><div><br></div><div>I'm not concerned about ^ because eventually Pulp3 will get authZ features which will allow administrators to prevent multi-user write-write races. Two users competing to write shared data is a normal, multi-user system problem. Also with the User A and User B update example, both synchronous and asynchronous update approaches would produce the same outcomes, so I don't think this point motivates either one as being better.<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
<br>
><br>
> _Uncertain State_<br>
<span class="im HOEnZb">> The configuration of an importer is unpredictable if there is a waiting update task from another user.<br>
><br>
><br>
><br>
><br>
><br>
><br>
><br>
</span><div class="HOEnZb"><div class="h5">> ______________________________<wbr>_________________<br>
> Pulp-dev mailing list<br>
> <a href="mailto:Pulp-dev@redhat.com">Pulp-dev@redhat.com</a><br>
> <a href="https://www.redhat.com/mailman/listinfo/pulp-dev" rel="noreferrer" target="_blank">https://www.redhat.com/<wbr>mailman/listinfo/pulp-dev</a><br>
><br>
<br>
</div></div><br>______________________________<wbr>_________________<br>
Pulp-dev mailing list<br>
<a href="mailto:Pulp-dev@redhat.com">Pulp-dev@redhat.com</a><br>
<a href="https://www.redhat.com/mailman/listinfo/pulp-dev" rel="noreferrer" target="_blank">https://www.redhat.com/<wbr>mailman/listinfo/pulp-dev</a><br>
<br></blockquote></div><br></div></div></div>