| Posted | Nick | Remark | |
|---|---|---|---|
| #openstack-nova - 2017-12-22 | |||
| 15:47:21 | mriedem | "recheck powervm" i guess | |
| 15:47:45 | mdbooth | It sounded like I hadn't convinced you, yet. I could be persuaded to simplify it, but I'm still thinking about that. | |
| 15:47:46 | artom | mdbooth, so, I grok the Python threads stuff - they don't run concurrently, one will run when another yields to do IO or whatever | |
| 15:48:03 | artom | mdbooth, it's not so much convincing, as explaining :) | |
| 15:48:32 | artom | mdbooth, what's not clear to me, and it may be ignorance of Python's internals on my part, is how your code makes sure there are two threads | |
| 15:48:37 | mdbooth | artom: Well your point is around whether the complexity is worth it, right? | |
| 15:48:56 | mdbooth | I'm confident it's a valid test. I could be persuaded that a simpler test might be better, though. | |
| 15:49:08 | mdbooth | There aren't 2 threads. | |
| 15:49:22 | mdbooth | There's only 1 thread, but it acts like 2 threads. | |
| 15:49:45 | mdbooth | We use mock to intercept a function call in the 'main' thread. | |
| 15:50:05 | mdbooth | When the main thread executes that function call, we interrupt it and do something else first, before continuing. | |
| 15:50:23 | mdbooth | Does that make more sense? | |
| 15:50:55 | mriedem | seems pretty paranoid for something that we've already established a pattern of in several other objects | |
| 15:51:06 | mriedem | or are you actually seeing this race happening with something like the cellsv1 job? | |
| 15:51:40 | artom | mdbooth, let me look at the code again | |
| 15:51:53 | artom | I still can't wrap my head around how purely sequential execution can test a race | |
| 15:51:58 | mdbooth | mriedem: It's more that I can see the bug and I fixed it, and it's not *that* complicated. | |
| 15:52:25 | mdbooth | mriedem: artom is trying to get his head round the test, and I've never yet managed to write a unit test for a race which was easy to read. | |
| 15:52:31 | artom | If it's just about calling _create_uuid twice, surely can do that without the whole flip/race thing | |
| 15:53:28 | mdbooth | artom: That's where I could be persuaded. Except that my test is 1 step up from that. | |
| 15:54:00 | mdbooth | My test asserts that if the race happens whilst reading the bdm object, it will work fine. | |
| 15:54:20 | mdbooth | Your test would be much simpler, and we could possibly agree it's sufficient. | |
| 15:54:24 | artom | But... the race can only happen when writing | |
| 15:54:55 | artom | I guess if you go up one stop from that, it two reads happen at the same time on the same uuid-less BDM, both will attempt to write a UUID | |
| 15:54:59 | artom | *if two | |
| 15:55:02 | mdbooth | No, this is weird. If we *read* a bdm object with no uuid we create one before returning it. | |
| 15:55:28 | mdbooth | So it's a read operation, but we might have to migrate a legacy object during it. | |
| 15:55:32 | artom | Right, but the actual race is the writing part | |
| 15:55:37 | mdbooth | Yes | |
| 15:56:01 | artom | That's what the code, does, right? Read, if there's no UUID, create one and save it, then return | |
| 15:56:02 | mdbooth | My test is at the level of the read operation | |
| 15:56:04 | mdbooth | Your test would just be on the write bit | |
| 15:56:37 | mdbooth | artom: Yep. Notice there's a compare-and-swap in there, which avoids a race. | |
| 15:57:18 | mdbooth | I guess the most important thing is to test the compare-and-swap, which simply executing _create_uuid manually twice would do. | |
| 15:57:24 | mdbooth | The test would also be easier to read. | |
| 15:57:39 | artom | Ah, right, compare and swap is atomic at the DB level, right? | |
| 15:57:45 | mdbooth | Yes | |
| 15:58:02 | mdbooth | There are a couple of db-related hoops in that function which ensure it's atomic. | |
| 15:58:14 | openstackgerrit | Merged openstack/nova stable/ocata: Use instance.project_id when creating request specs for old instances https://review.openstack.org/529387 | |
| 15:58:24 | artom | So if there are two of more getting compare_and_swaps getting to the DB at the same time, only the first one will succeed | |
| 15:58:37 | artom | So, the DB-write race is handled for us | |
| 15:58:42 | artom | I may have been thinking about this wrong | |
| 15:58:43 | mdbooth | Correct. | |
| 15:59:27 | artom | So is there actually a race then? If we try a compare_and_swap and it fails, if we read after that, we're guaranteed to get the correct UUID | |
| 15:59:43 | artom | A read race, I should say | |
| 15:59:51 | mdbooth | There is no read race. | |
| 15:59:59 | mdbooth | Not in this patch, anyway. | |
| 16:00:04 | artom | ... | |
| 16:00:10 | artom | So what are we testing then? | |
| 16:00:16 | mdbooth | However, I think the original one just wrote the uuid to the db. | |
| 16:00:28 | mdbooth | That is a race, and if you executed my test against that version, it would fail. | |
| 16:00:49 | openstackgerrit | Stephen Finucane proposed openstack/nova master: trivial: Modify signature of _filter_non_requested_pfs https://review.openstack.org/527473 | |
| 16:00:49 | openstackgerrit | Stephen Finucane proposed openstack/nova master: Add PCI NUMA policies https://review.openstack.org/527472 | |
| 16:01:08 | finucannot | leakypipes, bauwser: Voilà ^^ | |
| 16:01:13 | mdbooth | In fact, I think the original version didn't write it immediately at all, just generated it. | |
| 16:01:16 | leakypipes | mriedem: yeah, dinner was great. dishwasher is leaking, though... | |
| 16:01:44 | mdbooth | So there was a delay of unknown size between generation of the value and writing it to the db without a compare-and-swap. | |
| 16:01:45 | mriedem | leakypipes: "dishwasher" isn't code for one of the pugs is it? | |
| 16:01:47 | finucannot | leakypipes: I assume the irony of that has already been pointed out multiple times | |
| 16:01:47 | leakypipes | mriedem: so after $400 electrician bill I now have another $200 bill for the installers from Lowe's to come day after christmas to fix it. | |
| 16:01:53 | leakypipes | finucannot: yes sir. | |
| 16:02:00 | leakypipes | mriedem: no. | |
| 16:02:12 | artom | mdbooth, oh, so you're not testing that your code in the latest patchset handles a race properly | |
| 16:02:19 | mdbooth | artom: Yes. | |
| 16:02:29 | mdbooth | Erm... | |
| 16:02:33 | artom | Yes you're not? | |
| 16:02:35 | mdbooth | No, that's exactly what I'm testing. | |
| 16:02:35 | mriedem | leakypipes: oh lowes installers huh? i had to have lowes guys come back to fix our washing machine install which sprayed water all over the (thankfully unfinished) storage room in our basement | |
| 16:02:42 | mriedem | turns out zip ties are important for the drain hose | |
| 16:02:55 | artom | But you said there's no race in this patch | |
| 16:03:23 | mdbooth | The test asserts that the code correctly handles the situation where 2 threads both read a NULL uuid from the db, and both then try to populate it. | |
| 16:03:42 | mdbooth | The test passes. | |
| 16:03:51 | mdbooth | That doesn't mean there's no reason for the test, though. | |
| 16:04:59 | mdbooth | As it happens, the code will also work if the race happens during the db transaction, or on commit because both transactions occurred on different masters of a multi-master galera cluster. | |
| 16:05:04 | artom | SO I guess step one would be to remove all mentions of race from that test | |
| 16:05:07 | mdbooth | I didn't write tests for those, though, because.... | |
| 16:05:24 | mdbooth | I would only understand them very briefly myself. | |
| 16:05:39 | mdbooth | artom: No, because it's a race ;) | |
| 16:05:52 | mdbooth | It simulates a race | |
| 16:06:04 | mdbooth | It simulates 2 threads with 1 thread. | |
| 16:06:12 | artom | I'm going to look up the definition of race, I swear to God ;) | |
| 16:06:36 | mdbooth | A race is a bug caused by execution timing. | |
| 16:07:02 | mdbooth | It doesn't require real concurrency. | |
| 16:07:23 | artom | Right, when the outcome is determined by the order in which things are executed | |
| 16:07:47 | artom | The code is written so as to *not have that* | |
| 16:08:04 | artom | Because 1. compare_and_swap 2. if it fails, we handle it gracefully and read the correct value | |
| 16:08:08 | mdbooth | Yes, although order may be A->B, B->A, or A(a bit)->B(all of it)->A(the rest) | |
| 16:08:40 | figleaf | mriedem: not zip ties. Use these: https://images-na.ssl-images-amazon.com/images/I/71%2BzOxf-hBL._SX425_.jpg | |
| 16:09:19 | mdbooth | However, I could be convinced to test only the simpler consecutives calls to _create_uuid() | |
| 16:09:19 | artom | So given two reads A and B, we need a test that makes sure that, regardless of the sequence in which they're (partially) executed, the UUID read at the end is the same for both | |
| 16:09:39 | mdbooth | With a note that this simulates a race in the calling code, as this will only ever happen if there was a race. | |
| 16:10:07 | mdbooth | Rather than creating the race itself with fancy mocks. | |
| 16:10:15 | mriedem | figleaf: i used this https://s7d1.scene7.com/is/image/BedBathandBeyond/45894242895794p?$478$ to call lowes and tell them to get their guys out to fix the shit | |
| 16:10:48 | mriedem | ooo btw, where is my xmas list? | |
| 16:10:49 | mriedem | https://www.saksfifthavenue.com/main/ProductDetail.jsp?PRODUCT%3C%3Eprd_id=845524447125273&site_refer=CSE_GGLPRADS001&gclid=Cj0KCQiA9_LRBRDZARIsAAcLXjfz8bCutBnNrpZ-bKlG_1RRPKyVGX4G1WC1vjzOtoO6K81rPpJXYrgaAuXkEALw_wcB&gclsrc=aw.ds | |
| 16:11:01 | mdbooth | artom: Think of it like this. We've got code that does: if uuid is not populated: populate_uuid() | |
| 16:11:05 | mriedem | if i were a corporation with some sweet tax cuts i could buy that phone | |
| 16:11:27 | mriedem | oh it's not an actual phone | |
| 16:11:30 | mriedem | it's a makeup thingy | |
| 16:11:42 | mdbooth | artom: We're simulating the case where the above code is interrupted mid flow, so by the time populate_uuid is called, it's actually already populated. | |