* [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery @ 2020-04-30 19:27 Nikita Pettik 2020-04-30 19:27 ` [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection Nikita Pettik ` (3 more replies) 0 siblings, 4 replies; 25+ messages in thread From: Nikita Pettik @ 2020-04-30 19:27 UTC (permalink / raw) To: tarantool-patches; +Cc: v.shpilevoy Branch: https://github.com/tarantool/tarantool/commits/np/gh-4805-too-many-fds Issue: https://github.com/tarantool/tarantool/issues/4805 First patch adds simple macro which allows error injection to be delayed. It also can be used in this series: https://lists.tarantool.org/pipermail/tarantool-patches/2020-April/016367.html Nikita Pettik (2): errinj: introduce delayed injection vinyl: drop wasted runs in case range recovery fails src/box/vy_lsm.c | 14 ++- src/box/vy_run.c | 4 + src/errinj.h | 10 ++ test/box/errinj.result | 1 + test/vinyl/errinj_recovery.lua | 10 ++ .../gh-4805-open-run-err-recovery.result | 101 ++++++++++++++++++ .../gh-4805-open-run-err-recovery.test.lua | 38 +++++++ test/vinyl/suite.ini | 2 +- 8 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 test/vinyl/errinj_recovery.lua create mode 100644 test/vinyl/gh-4805-open-run-err-recovery.result create mode 100644 test/vinyl/gh-4805-open-run-err-recovery.test.lua -- 2.17.1 ^ permalink raw reply [flat|nested] 25+ messages in thread
* [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection 2020-04-30 19:27 [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery Nikita Pettik @ 2020-04-30 19:27 ` Nikita Pettik 2020-04-30 20:15 ` Konstantin Osipov 2020-05-03 19:20 ` Vladislav Shpilevoy 2020-04-30 19:27 ` [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails Nikita Pettik ` (2 subsequent siblings) 3 siblings, 2 replies; 25+ messages in thread From: Nikita Pettik @ 2020-04-30 19:27 UTC (permalink / raw) To: tarantool-patches; +Cc: v.shpilevoy With new macro ERROR_INJECT_DELAYED it is possible to delay error injection by DELAY parameter: injection will be set only after DELAY times the path is executed. For instance: void foo(int i) { /* 2 is delay counter. */ ERROR_INJECT_DELAYED(ERRINJ_FOO, 2, { printf("Error injection on %d cycle!\n", i); }); } void boo(void) { for (int i = 0; i < 10; ++i) foo(i); } The result is "Error injection on 2 cycle!". This type of error injection can turn out to be useful to set injection in the middle of query processing. Imagine following scenario: void foo(void) { int *fds[10]; for (int i = 0; i < 10; ++i) { fds[i] = malloc(sizeof(int)); if (fds[i] == NULL) goto cleanup; } cleanup: free(fds[0]); } "cleanup" section obviously contains error and leads to memory leak. But using means of casual error injection without delay such situation can't be detected: OOM can be set only for first cycle iteration and in this particular case no leaks take place. --- src/errinj.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/errinj.h b/src/errinj.h index 2cb090b68..990f4921d 100644 --- a/src/errinj.h +++ b/src/errinj.h @@ -149,6 +149,7 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); #ifdef NDEBUG # define ERROR_INJECT(ID, CODE) # define errinj(ID, TYPE) ((struct errinj *) NULL) +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) #else # /* Returns the error injection by id */ # define errinj(ID, TYPE) \ @@ -162,6 +163,14 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); if (errinj(ID, ERRINJ_BOOL)->bparam) \ CODE; \ } while (0) +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) \ + do { \ + static int _DELAY##ID = DELAY; \ + if (errinj(ID, ERRINJ_BOOL)->bparam) { \ + if (_DELAY##ID-- == 0) \ + CODE; \ + } \ + } while (0) #endif #define ERROR_INJECT_RETURN(ID) ERROR_INJECT(ID, return -1) -- 2.17.1 ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection 2020-04-30 19:27 ` [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection Nikita Pettik @ 2020-04-30 20:15 ` Konstantin Osipov 2020-04-30 20:55 ` Nikita Pettik 2020-05-03 19:20 ` Vladislav Shpilevoy 1 sibling, 1 reply; 25+ messages in thread From: Konstantin Osipov @ 2020-04-30 20:15 UTC (permalink / raw) To: Nikita Pettik; +Cc: tarantool-patches, v.shpilevoy * Nikita Pettik <korablev@tarantool.org> [20/04/30 22:51]: This is not exactly a time delay, this is a skip-first-n kind of postponed enable? Having a time delay is not such a bad idea (as well as a probabilistic failure). > With new macro ERROR_INJECT_DELAYED it is possible to delay error > injection by DELAY parameter: injection will be set only after DELAY > times the path is executed. For instance: > > void > foo(int i) > { > /* 2 is delay counter. */ > ERROR_INJECT_DELAYED(ERRINJ_FOO, 2, { > printf("Error injection on %d cycle!\n", i); > }); > } > > void > boo(void) > { > for (int i = 0; i < 10; ++i) > foo(i); > } > > The result is "Error injection on 2 cycle!". This type of error > injection can turn out to be useful to set injection in the middle of > query processing. Imagine following scenario: > > void > foo(void) > { > int *fds[10]; > for (int i = 0; i < 10; ++i) { > fds[i] = malloc(sizeof(int)); > if (fds[i] == NULL) > goto cleanup; > } > cleanup: > free(fds[0]); > } > > "cleanup" section obviously contains error and leads to memory leak. > But using means of casual error injection without delay such situation > can't be detected: OOM can be set only for first cycle iteration and in > this particular case no leaks take place. > --- > src/errinj.h | 9 +++++++++ > 1 file changed, 9 insertions(+) > > diff --git a/src/errinj.h b/src/errinj.h > index 2cb090b68..990f4921d 100644 > --- a/src/errinj.h > +++ b/src/errinj.h > @@ -149,6 +149,7 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > #ifdef NDEBUG > # define ERROR_INJECT(ID, CODE) > # define errinj(ID, TYPE) ((struct errinj *) NULL) > +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) > #else > # /* Returns the error injection by id */ > # define errinj(ID, TYPE) \ > @@ -162,6 +163,14 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > if (errinj(ID, ERRINJ_BOOL)->bparam) \ > CODE; \ > } while (0) > +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) \ > + do { \ > + static int _DELAY##ID = DELAY; \ > + if (errinj(ID, ERRINJ_BOOL)->bparam) { \ > + if (_DELAY##ID-- == 0) \ > + CODE; \ > + } \ > + } while (0) > #endif > > #define ERROR_INJECT_RETURN(ID) ERROR_INJECT(ID, return -1) > -- > 2.17.1 > -- Konstantin Osipov, Moscow, Russia https://scylladb.com ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection 2020-04-30 20:15 ` Konstantin Osipov @ 2020-04-30 20:55 ` Nikita Pettik 2020-05-01 19:15 ` Konstantin Osipov 0 siblings, 1 reply; 25+ messages in thread From: Nikita Pettik @ 2020-04-30 20:55 UTC (permalink / raw) To: Konstantin Osipov, tarantool-patches, v.shpilevoy, alyapunov On 30 Apr 23:15, Konstantin Osipov wrote: > * Nikita Pettik <korablev@tarantool.org> [20/04/30 22:51]: > > This is not exactly a time delay, this is a skip-first-n kind of > postponed enable? Yep, it's more accurate definition. I can rename to ERROR_INJECT_POSPONED, if it fits better. > Having a time delay is not such a bad idea (as well as a > probabilistic failure). > > > With new macro ERROR_INJECT_DELAYED it is possible to delay error > > injection by DELAY parameter: injection will be set only after DELAY > > times the path is executed. For instance: > > > > void > > foo(int i) > > { > > /* 2 is delay counter. */ > > ERROR_INJECT_DELAYED(ERRINJ_FOO, 2, { > > printf("Error injection on %d cycle!\n", i); > > }); > > } > > > > void > > boo(void) > > { > > for (int i = 0; i < 10; ++i) > > foo(i); > > } > > > > The result is "Error injection on 2 cycle!". This type of error > > injection can turn out to be useful to set injection in the middle of > > query processing. Imagine following scenario: > > > > void > > foo(void) > > { > > int *fds[10]; > > for (int i = 0; i < 10; ++i) { > > fds[i] = malloc(sizeof(int)); > > if (fds[i] == NULL) > > goto cleanup; > > } > > cleanup: > > free(fds[0]); > > } > > > > "cleanup" section obviously contains error and leads to memory leak. > > But using means of casual error injection without delay such situation > > can't be detected: OOM can be set only for first cycle iteration and in > > this particular case no leaks take place. > > --- > > src/errinj.h | 9 +++++++++ > > 1 file changed, 9 insertions(+) > > > > diff --git a/src/errinj.h b/src/errinj.h > > index 2cb090b68..990f4921d 100644 > > --- a/src/errinj.h > > +++ b/src/errinj.h > > @@ -149,6 +149,7 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > > #ifdef NDEBUG > > # define ERROR_INJECT(ID, CODE) > > # define errinj(ID, TYPE) ((struct errinj *) NULL) > > +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) > > #else > > # /* Returns the error injection by id */ > > # define errinj(ID, TYPE) \ > > @@ -162,6 +163,14 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > > if (errinj(ID, ERRINJ_BOOL)->bparam) \ > > CODE; \ > > } while (0) > > +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) \ > > + do { \ > > + static int _DELAY##ID = DELAY; \ > > + if (errinj(ID, ERRINJ_BOOL)->bparam) { \ > > + if (_DELAY##ID-- == 0) \ > > + CODE; \ > > + } \ > > + } while (0) > > #endif > > > > #define ERROR_INJECT_RETURN(ID) ERROR_INJECT(ID, return -1) > > -- > > 2.17.1 > > > > -- > Konstantin Osipov, Moscow, Russia > https://scylladb.com ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection 2020-04-30 20:55 ` Nikita Pettik @ 2020-05-01 19:15 ` Konstantin Osipov 0 siblings, 0 replies; 25+ messages in thread From: Konstantin Osipov @ 2020-05-01 19:15 UTC (permalink / raw) To: Nikita Pettik; +Cc: tarantool-patches, v.shpilevoy * Nikita Pettik <korablev@tarantool.org> [20/04/30 23:58]: > On 30 Apr 23:15, Konstantin Osipov wrote: > > * Nikita Pettik <korablev@tarantool.org> [20/04/30 22:51]: > > > > This is not exactly a time delay, this is a skip-first-n kind of > > postponed enable? > > Yep, it's more accurate definition. I can rename to > ERROR_INJECT_POSPONED, if it fits better. postponed ERROR_INJECT_NTH ERROR_INJECT_COUNTDOWN -- Konstantin Osipov, Moscow, Russia https://scylladb.com ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection 2020-04-30 19:27 ` [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection Nikita Pettik 2020-04-30 20:15 ` Konstantin Osipov @ 2020-05-03 19:20 ` Vladislav Shpilevoy 2020-05-07 13:50 ` Nikita Pettik 1 sibling, 1 reply; 25+ messages in thread From: Vladislav Shpilevoy @ 2020-05-03 19:20 UTC (permalink / raw) To: Nikita Pettik, tarantool-patches Thanks for the patch! > diff --git a/src/errinj.h b/src/errinj.h > index 2cb090b68..990f4921d 100644 > --- a/src/errinj.h > +++ b/src/errinj.h > @@ -149,6 +149,7 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > #ifdef NDEBUG > # define ERROR_INJECT(ID, CODE) > # define errinj(ID, TYPE) ((struct errinj *) NULL) > +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) > #else > # /* Returns the error injection by id */ > # define errinj(ID, TYPE) \ > @@ -162,6 +163,14 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > if (errinj(ID, ERRINJ_BOOL)->bparam) \ > CODE; \ > } while (0) > +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) \ > + do { \ > + static int _DELAY##ID = DELAY; \ > + if (errinj(ID, ERRINJ_BOOL)->bparam) { \ > + if (_DELAY##ID-- == 0) \ > + CODE; \ > + } \ > + } while (0) The method is fine unless you will ever want to set the same error injection twice without restarting Tarantool. With this solution there is no way to reset _DELAY##ID back to the same or a different value. It will stay 0 until restart. This a serious enough problem to reconsider the approach. There are injections used in multiple tests, and we can't count on restart after each one. This is the reason why you won't be able to use this delayed injection for VY_STMT_ALLOC in 'uninitialized memory' patchset. 'Delayed' easily can be implemented by iparam. For example, look at ERRINJ_WAL_FALLOCATE and your own VY_STMT_ALLOC. You set iparam to some value, then you decrement it until 0, and inject the error at this moment. Works perfectly fine, and without introducing any new unrecoverable ERRINJ_ counters. However if you want to simplify that, just wrap the iparam-method into a macros. Even the same name could be fine - ERROR_INJECT_DELAYED. Moreover, it simplifies check that the injection really happened, and its progress. You just look at iparam in Lua and see how many times it has executed, and whether it is equal to 0 already (with bparam you can do the same though, by setting it to false explicitly). ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection 2020-05-03 19:20 ` Vladislav Shpilevoy @ 2020-05-07 13:50 ` Nikita Pettik 2020-05-07 21:47 ` Vladislav Shpilevoy 0 siblings, 1 reply; 25+ messages in thread From: Nikita Pettik @ 2020-05-07 13:50 UTC (permalink / raw) To: Vladislav Shpilevoy; +Cc: tarantool-patches On 03 May 21:20, Vladislav Shpilevoy wrote: > Thanks for the patch! > > > diff --git a/src/errinj.h b/src/errinj.h > > index 2cb090b68..990f4921d 100644 > > --- a/src/errinj.h > > +++ b/src/errinj.h > > @@ -149,6 +149,7 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > > #ifdef NDEBUG > > # define ERROR_INJECT(ID, CODE) > > # define errinj(ID, TYPE) ((struct errinj *) NULL) > > +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) > > #else > > # /* Returns the error injection by id */ > > # define errinj(ID, TYPE) \ > > @@ -162,6 +163,14 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > > if (errinj(ID, ERRINJ_BOOL)->bparam) \ > > CODE; \ > > } while (0) > > +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) \ > > + do { \ > > + static int _DELAY##ID = DELAY; \ > > + if (errinj(ID, ERRINJ_BOOL)->bparam) { \ > > + if (_DELAY##ID-- == 0) \ > > + CODE; \ > > + } \ > > + } while (0) > > The method is fine unless you will ever want to set the same > error injection twice without restarting Tarantool. With this > solution there is no way to reset _DELAY##ID back to the same or > a different value. It will stay 0 until restart. This a serious > enough problem to reconsider the approach. There are injections > used in multiple tests, and we can't count on restart after each > one. Yep, this is obvious drawback of chosen implementation. > This is the reason why you won't be able to use this delayed > injection for VY_STMT_ALLOC in 'uninitialized memory' patchset. > > 'Delayed' easily can be implemented by iparam. For example, look > at ERRINJ_WAL_FALLOCATE and your own VY_STMT_ALLOC. You set > iparam to some value, then you decrement it until 0, and inject > the error at this moment. Works perfectly fine, and without > introducing any new unrecoverable ERRINJ_ counters. However if you > want to simplify that, just wrap the iparam-method into a macros. > Even the same name could be fine - ERROR_INJECT_DELAYED. Ok, here's diff: diff --git a/src/errinj.h b/src/errinj.h index 990f4921d..945abb939 100644 --- a/src/errinj.h +++ b/src/errinj.h @@ -163,12 +163,10 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); if (errinj(ID, ERRINJ_BOOL)->bparam) \ CODE; \ } while (0) -# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) \ +# define ERROR_INJECT_COUNTDOWN(ID, CODE) \ do { \ - static int _DELAY##ID = DELAY; \ - if (errinj(ID, ERRINJ_BOOL)->bparam) { \ - if (_DELAY##ID-- == 0) \ - CODE; \ + if (errinj(ID, ERRINJ_INT)->iparam-- == 0) { \ + CODE; \ } \ } while (0) #endif > Moreover, it simplifies check that the injection really happened, > and its progress. You just look at iparam in Lua and see how many > times it has executed, and whether it is equal to 0 already > (with bparam you can do the same though, by setting it to false > explicitly). ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection 2020-05-07 13:50 ` Nikita Pettik @ 2020-05-07 21:47 ` Vladislav Shpilevoy 2020-05-07 22:41 ` Nikita Pettik 0 siblings, 1 reply; 25+ messages in thread From: Vladislav Shpilevoy @ 2020-05-07 21:47 UTC (permalink / raw) To: Nikita Pettik; +Cc: tarantool-patches Hi! Thanks for the fixes! >>> diff --git a/src/errinj.h b/src/errinj.h >>> index 2cb090b68..990f4921d 100644 >>> --- a/src/errinj.h >>> +++ b/src/errinj.h >>> @@ -149,6 +149,7 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); >>> #ifdef NDEBUG >>> # define ERROR_INJECT(ID, CODE) >>> # define errinj(ID, TYPE) ((struct errinj *) NULL) >>> +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) >>> #else >>> # /* Returns the error injection by id */ >>> # define errinj(ID, TYPE) \ >>> @@ -162,6 +163,14 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); >>> if (errinj(ID, ERRINJ_BOOL)->bparam) \ >>> CODE; \ >>> } while (0) >>> +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) \ >>> + do { \ >>> + static int _DELAY##ID = DELAY; \ >>> + if (errinj(ID, ERRINJ_BOOL)->bparam) { \ >>> + if (_DELAY##ID-- == 0) \ >>> + CODE; \ >>> + } \ >>> + } while (0) >> >> The method is fine unless you will ever want to set the same >> error injection twice without restarting Tarantool. With this >> solution there is no way to reset _DELAY##ID back to the same or >> a different value. It will stay 0 until restart. This a serious >> enough problem to reconsider the approach. There are injections >> used in multiple tests, and we can't count on restart after each >> one. > > Yep, this is obvious drawback of chosen implementation. > >> This is the reason why you won't be able to use this delayed >> injection for VY_STMT_ALLOC in 'uninitialized memory' patchset. >> >> 'Delayed' easily can be implemented by iparam. For example, look >> at ERRINJ_WAL_FALLOCATE and your own VY_STMT_ALLOC. You set >> iparam to some value, then you decrement it until 0, and inject >> the error at this moment. Works perfectly fine, and without >> introducing any new unrecoverable ERRINJ_ counters. However if you >> want to simplify that, just wrap the iparam-method into a macros. >> Even the same name could be fine - ERROR_INJECT_DELAYED. > > Ok, here's diff: > > diff --git a/src/errinj.h b/src/errinj.h > index 990f4921d..945abb939 100644 > --- a/src/errinj.h > +++ b/src/errinj.h > @@ -163,12 +163,10 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > if (errinj(ID, ERRINJ_BOOL)->bparam) \ > CODE; \ > } while (0) > -# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) \ > +# define ERROR_INJECT_COUNTDOWN(ID, CODE) \ > do { \ > - static int _DELAY##ID = DELAY; \ > - if (errinj(ID, ERRINJ_BOOL)->bparam) { \ > - if (_DELAY##ID-- == 0) \ > - CODE; \ > + if (errinj(ID, ERRINJ_INT)->iparam-- == 0) { \ > + CODE; \ > } \ > } while (0) > #endif One last thing - could you please align '\' to the end of the lines? By 80 symbols, using tabs. Recently we had a discussion with Cyrill G, and he mentioned such not aligned things are hard to read. Physically. Eyes literally are getting tired faster. He also wants us to align struct members everywhere, but that is a different topic to discuss. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection 2020-05-07 21:47 ` Vladislav Shpilevoy @ 2020-05-07 22:41 ` Nikita Pettik 0 siblings, 0 replies; 25+ messages in thread From: Nikita Pettik @ 2020-05-07 22:41 UTC (permalink / raw) To: Vladislav Shpilevoy; +Cc: tarantool-patches On 07 May 23:47, Vladislav Shpilevoy wrote: > Hi! Thanks for the fixes! > > >>> diff --git a/src/errinj.h b/src/errinj.h > >>> index 2cb090b68..990f4921d 100644 > >>> --- a/src/errinj.h > >>> +++ b/src/errinj.h > >>> @@ -149,6 +149,7 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > >>> #ifdef NDEBUG > >>> # define ERROR_INJECT(ID, CODE) > >>> # define errinj(ID, TYPE) ((struct errinj *) NULL) > >>> +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) > >>> #else > >>> # /* Returns the error injection by id */ > >>> # define errinj(ID, TYPE) \ > >>> @@ -162,6 +163,14 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > >>> if (errinj(ID, ERRINJ_BOOL)->bparam) \ > >>> CODE; \ > >>> } while (0) > >>> +# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) \ > >>> + do { \ > >>> + static int _DELAY##ID = DELAY; \ > >>> + if (errinj(ID, ERRINJ_BOOL)->bparam) { \ > >>> + if (_DELAY##ID-- == 0) \ > >>> + CODE; \ > >>> + } \ > >>> + } while (0) > >> > >> The method is fine unless you will ever want to set the same > >> error injection twice without restarting Tarantool. With this > >> solution there is no way to reset _DELAY##ID back to the same or > >> a different value. It will stay 0 until restart. This a serious > >> enough problem to reconsider the approach. There are injections > >> used in multiple tests, and we can't count on restart after each > >> one. > > > > Yep, this is obvious drawback of chosen implementation. > > > >> This is the reason why you won't be able to use this delayed > >> injection for VY_STMT_ALLOC in 'uninitialized memory' patchset. > >> > >> 'Delayed' easily can be implemented by iparam. For example, look > >> at ERRINJ_WAL_FALLOCATE and your own VY_STMT_ALLOC. You set > >> iparam to some value, then you decrement it until 0, and inject > >> the error at this moment. Works perfectly fine, and without > >> introducing any new unrecoverable ERRINJ_ counters. However if you > >> want to simplify that, just wrap the iparam-method into a macros. > >> Even the same name could be fine - ERROR_INJECT_DELAYED. > > > > Ok, here's diff: > > > > diff --git a/src/errinj.h b/src/errinj.h > > index 990f4921d..945abb939 100644 > > --- a/src/errinj.h > > +++ b/src/errinj.h > > @@ -163,12 +163,10 @@ errinj_foreach(errinj_cb cb, void *cb_ctx); > > if (errinj(ID, ERRINJ_BOOL)->bparam) \ > > CODE; \ > > } while (0) > > -# define ERROR_INJECT_DELAYED(ID, DELAY, CODE) \ > > +# define ERROR_INJECT_COUNTDOWN(ID, CODE) \ > > do { \ > > - static int _DELAY##ID = DELAY; \ > > - if (errinj(ID, ERRINJ_BOOL)->bparam) { \ > > - if (_DELAY##ID-- == 0) \ > > - CODE; \ > > + if (errinj(ID, ERRINJ_INT)->iparam-- == 0) { \ > > + CODE; \ > > } \ > > } while (0) > > #endif > > One last thing - could you please align '\' to the end of the lines? > By 80 symbols, using tabs. Recently we had a discussion with Cyrill G, My editor doesn't provide an ability to align by custom offset using tabs, so I aligned \ by 74 symbols. Feel free to force push fix if it still looks wrong. > and he mentioned such not aligned things are hard to read. Physically. > Eyes literally are getting tired faster. > > He also wants us to align struct members everywhere, but that is a > different topic to discuss. ^ permalink raw reply [flat|nested] 25+ messages in thread
* [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails 2020-04-30 19:27 [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery Nikita Pettik 2020-04-30 19:27 ` [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection Nikita Pettik @ 2020-04-30 19:27 ` Nikita Pettik 2020-05-03 19:21 ` Vladislav Shpilevoy 2020-05-03 19:20 ` [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery Vladislav Shpilevoy 2020-05-12 20:53 ` Vladislav Shpilevoy 3 siblings, 1 reply; 25+ messages in thread From: Nikita Pettik @ 2020-04-30 19:27 UTC (permalink / raw) To: tarantool-patches; +Cc: v.shpilevoy If recovery process fails during range restoration, range itself is deleted and recovery is assumed to be finished as failed (in case of casual i.e. not forced recovery). During recovery of particular range, runs to be restored are reffed twice: once when they are created at vy_run_new() and once when they are attached to slice. This fact is taken into consideration and after all ranges are recovered all runs of lsm tree are unreffed so that slices own run resources. However, if range recovery fails, it is dropped alongside with slices which in turn results in unreffing runs - this is not accounted. In this case, once again unreffing such runs would lead to their destruction. On the other hand, iteration over runs may turn out to be unsafe, so we should use rlist_foreach_entry_safe(). Moreover, we should explicitly unaccount these runs calling vy_lsm_remove_run(). Closes #4805 --- src/box/vy_lsm.c | 14 ++- src/box/vy_run.c | 4 + src/errinj.h | 1 + test/box/errinj.result | 1 + test/vinyl/errinj_recovery.lua | 10 ++ .../gh-4805-open-run-err-recovery.result | 101 ++++++++++++++++++ .../gh-4805-open-run-err-recovery.test.lua | 38 +++++++ test/vinyl/suite.ini | 2 +- 8 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 test/vinyl/errinj_recovery.lua create mode 100644 test/vinyl/gh-4805-open-run-err-recovery.result create mode 100644 test/vinyl/gh-4805-open-run-err-recovery.test.lua diff --git a/src/box/vy_lsm.c b/src/box/vy_lsm.c index 3d3f41b7a..81b011c69 100644 --- a/src/box/vy_lsm.c +++ b/src/box/vy_lsm.c @@ -604,9 +604,17 @@ vy_lsm_recover(struct vy_lsm *lsm, struct vy_recovery *recovery, * of each recovered run. We need to drop the extra * references once we are done. */ - struct vy_run *run; - rlist_foreach_entry(run, &lsm->runs, in_lsm) { - assert(run->refs > 1); + struct vy_run *run, *next_run; + rlist_foreach_entry_safe(run, &lsm->runs, in_lsm, next_run) { + /* + * In case vy_lsm_recover_range() failed, slices + * are already deleted and runs are unreffed. So + * we have nothing to do but finish run clean-up. + */ + if (run->refs == 1) { + assert(rc != 0); + vy_lsm_remove_run(lsm, run); + } vy_run_unref(run); } diff --git a/src/box/vy_run.c b/src/box/vy_run.c index bd8a7a430..eeaeb88b4 100644 --- a/src/box/vy_run.c +++ b/src/box/vy_run.c @@ -1676,6 +1676,10 @@ vy_run_recover(struct vy_run *run, const char *dir, space_id, iid, run->id, VY_FILE_INDEX); struct xlog_cursor cursor; + ERROR_INJECT_DELAYED(ERRINJ_VY_RUN_OPEN, 2, { + diag_set(SystemError, "failed to open '%s' file", path); + goto fail; + }); if (xlog_cursor_open(&cursor, path)) goto fail; diff --git a/src/errinj.h b/src/errinj.h index 990f4921d..273458d59 100644 --- a/src/errinj.h +++ b/src/errinj.h @@ -127,6 +127,7 @@ struct errinj { _(ERRINJ_VY_COMPACTION_DELAY, ERRINJ_BOOL, {.bparam = false}) \ _(ERRINJ_DYN_MODULE_COUNT, ERRINJ_INT, {.iparam = 0}) \ _(ERRINJ_INDEX_RESERVE, ERRINJ_BOOL, {.bparam = false})\ + _(ERRINJ_VY_RUN_OPEN, ERRINJ_BOOL, {.bparam = false})\ ENUM0(errinj_id, ERRINJ_LIST); extern struct errinj errinjs[]; diff --git a/test/box/errinj.result b/test/box/errinj.result index 8090deedc..785f6394b 100644 --- a/test/box/errinj.result +++ b/test/box/errinj.result @@ -70,6 +70,7 @@ evals - ERRINJ_VY_READ_PAGE_TIMEOUT: 0 - ERRINJ_VY_RUN_DISCARD: false - ERRINJ_VY_RUN_FILE_RENAME: false + - ERRINJ_VY_RUN_OPEN: false - ERRINJ_VY_RUN_WRITE: false - ERRINJ_VY_RUN_WRITE_DELAY: false - ERRINJ_VY_RUN_WRITE_STMT_TIMEOUT: 0 diff --git a/test/vinyl/errinj_recovery.lua b/test/vinyl/errinj_recovery.lua new file mode 100644 index 000000000..925d12a85 --- /dev/null +++ b/test/vinyl/errinj_recovery.lua @@ -0,0 +1,10 @@ +#!/usr/bin/env tarantool + +box.error.injection.set('ERRINJ_VY_RUN_OPEN', true) +assert(box.error.injection.get('ERRINJ_VY_RUN_OPEN')) + +box.cfg { + listen = os.getenv("LISTEN"), +} + +require('console').listen(os.getenv('ADMIN')) diff --git a/test/vinyl/gh-4805-open-run-err-recovery.result b/test/vinyl/gh-4805-open-run-err-recovery.result new file mode 100644 index 000000000..31e0e7857 --- /dev/null +++ b/test/vinyl/gh-4805-open-run-err-recovery.result @@ -0,0 +1,101 @@ +-- test-run result file version 2 +fiber = require('fiber') + | --- + | ... +test_run = require('test_run').new() + | --- + | ... + +test_run:cmd('create server err_recovery with script = "vinyl/errinj_recovery.lua"') + | --- + | - true + | ... +test_run:cmd('start server err_recovery') + | --- + | - true + | ... +test_run:cmd('switch err_recovery') + | --- + | - true + | ... + +s = box.schema.space.create('test', {engine = 'vinyl'}) + | --- + | ... +_ = s:create_index('pk', {run_count_per_level = 100, page_size = 128, range_size = 1024}) + | --- + | ... + +digest = require('digest') + | --- + | ... +test_run:cmd("setopt delimiter ';'") + | --- + | - true + | ... +function dump(big) + local step = big and 1 or 5 + for i = 1, 20, step do + s:replace{i, digest.urandom(1000)} + end + box.snapshot() +end; + | --- + | ... +test_run:cmd("setopt delimiter ''"); + | --- + | - true + | ... + +dump(true) + | --- + | ... +dump() + | --- + | ... +dump() + | --- + | ... + +test_run:cmd('switch default') + | --- + | - true + | ... +test_run:cmd('stop server err_recovery') + | --- + | - true + | ... +test_run:cmd('start server err_recovery with crash_expected=True') + | --- + | - false + | ... + +fio = require('fio') + | --- + | ... +fh = fio.open(fio.pathjoin(fio.cwd(), 'errinj_recovery.log'), {'O_RDONLY'}) + | --- + | ... +size = fh:seek(0, 'SEEK_END') + | --- + | ... +fh:seek(-256, 'SEEK_END') ~= nil + | --- + | - true + | ... +line = fh:read(256) + | --- + | ... +fh:close() + | --- + | - true + | ... +string.match(line, 'failed to open') ~= nil + | --- + | - true + | ... + +test_run:cmd('delete server err_recovery') + | --- + | - true + | ... diff --git a/test/vinyl/gh-4805-open-run-err-recovery.test.lua b/test/vinyl/gh-4805-open-run-err-recovery.test.lua new file mode 100644 index 000000000..8b5895d41 --- /dev/null +++ b/test/vinyl/gh-4805-open-run-err-recovery.test.lua @@ -0,0 +1,38 @@ +fiber = require('fiber') +test_run = require('test_run').new() + +test_run:cmd('create server err_recovery with script = "vinyl/errinj_recovery.lua"') +test_run:cmd('start server err_recovery') +test_run:cmd('switch err_recovery') + +s = box.schema.space.create('test', {engine = 'vinyl'}) +_ = s:create_index('pk', {run_count_per_level = 100, page_size = 128, range_size = 1024}) + +digest = require('digest') +test_run:cmd("setopt delimiter ';'") +function dump(big) + local step = big and 1 or 5 + for i = 1, 20, step do + s:replace{i, digest.urandom(1000)} + end + box.snapshot() +end; +test_run:cmd("setopt delimiter ''"); + +dump(true) +dump() +dump() + +test_run:cmd('switch default') +test_run:cmd('stop server err_recovery') +test_run:cmd('start server err_recovery with crash_expected=True') + +fio = require('fio') +fh = fio.open(fio.pathjoin(fio.cwd(), 'errinj_recovery.log'), {'O_RDONLY'}) +size = fh:seek(0, 'SEEK_END') +fh:seek(-256, 'SEEK_END') ~= nil +line = fh:read(256) +fh:close() +string.match(line, 'failed to open') ~= nil + +test_run:cmd('delete server err_recovery') diff --git a/test/vinyl/suite.ini b/test/vinyl/suite.ini index 1417d7156..ce02eb83d 100644 --- a/test/vinyl/suite.ini +++ b/test/vinyl/suite.ini @@ -2,7 +2,7 @@ core = tarantool description = vinyl integration tests script = vinyl.lua -release_disabled = errinj.test.lua errinj_ddl.test.lua errinj_gc.test.lua errinj_stat.test.lua errinj_tx.test.lua errinj_vylog.test.lua partial_dump.test.lua quota_timeout.test.lua recovery_quota.test.lua replica_rejoin.test.lua +release_disabled = errinj.test.lua errinj_ddl.test.lua errinj_gc.test.lua errinj_stat.test.lua errinj_tx.test.lua errinj_vylog.test.lua partial_dump.test.lua quota_timeout.test.lua recovery_quota.test.lua replica_rejoin.test.lua gh-4805-open-run-err-recovery.test.lua config = suite.cfg lua_libs = suite.lua stress.lua large.lua txn_proxy.lua ../box/lua/utils.lua use_unix_sockets = True -- 2.17.1 ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails 2020-04-30 19:27 ` [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails Nikita Pettik @ 2020-05-03 19:21 ` Vladislav Shpilevoy 2020-05-07 13:02 ` Nikita Pettik 0 siblings, 1 reply; 25+ messages in thread From: Vladislav Shpilevoy @ 2020-05-03 19:21 UTC (permalink / raw) To: Nikita Pettik, tarantool-patches Thanks for the patch! On 30/04/2020 21:27, Nikita Pettik wrote: > If recovery process fails during range restoration, range itself is > deleted and recovery is assumed to be finished as failed (in case of > casual i.e. not forced recovery). During recovery of particular range, > runs to be restored are reffed twice: once when they are created at > vy_run_new() and once when they are attached to slice. This fact is > taken into consideration and after all ranges are recovered all runs of > lsm tree are unreffed so that slices own run resources. However, if Nit: 'unreffed' -> 'unrefed'. Since this is a shortcut for 'unreferenced', where number of 'f' is 1. The same for 'reffed'. > range recovery fails, it is dropped alongside with slices which in turn > results in unreffing runs - this is not accounted. In this case, once > again unreffing such runs would lead to their destruction. On the other > hand, iteration over runs may turn out to be unsafe, so we should use > rlist_foreach_entry_safe(). Moreover, we should explicitly unaccount > these runs calling vy_lsm_remove_run(). Sorry, I didn't understand almost everything from the message :D But I did from the code. You may want to rephrase/restruct the text if you think it may help. > diff --git a/src/box/vy_lsm.c b/src/box/vy_lsm.c > index 3d3f41b7a..81b011c69 100644 > --- a/src/box/vy_lsm.c > +++ b/src/box/vy_lsm.c > @@ -604,9 +604,17 @@ vy_lsm_recover(struct vy_lsm *lsm, struct vy_recovery *recovery, > * of each recovered run. We need to drop the extra > * references once we are done. > */ > - struct vy_run *run; > - rlist_foreach_entry(run, &lsm->runs, in_lsm) { > - assert(run->refs > 1); > + struct vy_run *run, *next_run; > + rlist_foreach_entry_safe(run, &lsm->runs, in_lsm, next_run) { > + /* > + * In case vy_lsm_recover_range() failed, slices > + * are already deleted and runs are unreffed. So > + * we have nothing to do but finish run clean-up. > + */ > + if (run->refs == 1) { Reference counter looks like not a good information channel. Could you use run->fd to check whether the run was really recovered? vy_run_recover() leaves it -1, when fails. Otherwise this won't work the second when we will ref the run anywhere else. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails 2020-05-03 19:21 ` Vladislav Shpilevoy @ 2020-05-07 13:02 ` Nikita Pettik 2020-05-07 14:16 ` Konstantin Osipov 2020-05-07 21:47 ` Vladislav Shpilevoy 0 siblings, 2 replies; 25+ messages in thread From: Nikita Pettik @ 2020-05-07 13:02 UTC (permalink / raw) To: Vladislav Shpilevoy; +Cc: tarantool-patches On 03 May 21:21, Vladislav Shpilevoy wrote: > Thanks for the patch! > > On 30/04/2020 21:27, Nikita Pettik wrote: > > If recovery process fails during range restoration, range itself is > > deleted and recovery is assumed to be finished as failed (in case of > > casual i.e. not forced recovery). During recovery of particular range, > > runs to be restored are reffed twice: once when they are created at > > vy_run_new() and once when they are attached to slice. This fact is > > taken into consideration and after all ranges are recovered all runs of > > lsm tree are unreffed so that slices own run resources. However, if > > Nit: 'unreffed' -> 'unrefed'. Since this is a shortcut for > 'unreferenced', where number of 'f' is 1. > > The same for 'reffed'. Fixed. > > range recovery fails, it is dropped alongside with slices which in turn > > results in unreffing runs - this is not accounted. In this case, once > > again unreffing such runs would lead to their destruction. On the other > > hand, iteration over runs may turn out to be unsafe, so we should use > > rlist_foreach_entry_safe(). Moreover, we should explicitly unaccount > > these runs calling vy_lsm_remove_run(). > > Sorry, I didn't understand almost everything from the message :D > But I did from the code. You may want to rephrase/restruct the text > if you think it may help. A bit reworked commit message and provided a brief schema in pseudocode. Hope this will help: vinyl: drop wasted runs in case range recovery fails If recovery process fails during range restoration, range itself is deleted and recovery is assumed to be finished as failed (in case of casual i.e. not forced recovery). During recovery of particular range, runs to be restored are refed twice: once when they are created at vy_run_new() and once when they are attached to slice. This fact is taken into consideration and after all ranges are recovered: all runs of lsm tree are unrefed so that slices own run resources (as a result, when slice is to be deleted its runs unrefed and deleted as well). However, if range recovery fails, range is dropped alongside with already recovered slices. This leads to unrefing runs - this is not accounted. To sum up recovery process below is a brief schema: foreach range in lsm.ranges { vy_lsm_recover_range(range) { foreach slice in range.slices { // inside recover_slice() each run is refed twice if vy_lsm_recover_slice() != 0 { // here all already restored slices are deleted and // corresponding runs are unrefed, so now they have 1 ref. range_delete() } } } } foreach run in lsm.runs { assert(run->refs > 1) vy_run_unref(run) } In this case, unrefing such runs one more time would lead to their destruction. On the other hand, iteration over runs may turn out to be unsafe, so we should use rlist_foreach_entry_safe(). Moreover, we should explicitly clean-up these runs calling vy_lsm_remove_run(). Closes #4805 > > diff --git a/src/box/vy_lsm.c b/src/box/vy_lsm.c > > index 3d3f41b7a..81b011c69 100644 > > --- a/src/box/vy_lsm.c > > +++ b/src/box/vy_lsm.c > > @@ -604,9 +604,17 @@ vy_lsm_recover(struct vy_lsm *lsm, struct vy_recovery *recovery, > > * of each recovered run. We need to drop the extra > > * references once we are done. > > */ > > - struct vy_run *run; > > - rlist_foreach_entry(run, &lsm->runs, in_lsm) { > > - assert(run->refs > 1); > > + struct vy_run *run, *next_run; > > + rlist_foreach_entry_safe(run, &lsm->runs, in_lsm, next_run) { > > + /* > > + * In case vy_lsm_recover_range() failed, slices > > + * are already deleted and runs are unreffed. So > > + * we have nothing to do but finish run clean-up. > > + */ > > + if (run->refs == 1) { > > Reference counter looks like not a good information channel. > Could you use run->fd to check whether the run was really recovered? > vy_run_recover() leaves it -1, when fails. > > Otherwise this won't work the second when we will ref the run anywhere > else. Firstly, lsm at this point is not restored, ergo it is not functional and run can't be refed somewehere else - it's life span is clearly defined. Secondly, the problem is not in the last run (which failed to recover) but in those which are already recovered at the moment. Recovered runs feature valid fds. Finally, slice recover may fail not only in vy_run_recover(), but also due to oom, broken vylog etc. All these scenarios lead to the same situation. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails 2020-05-07 13:02 ` Nikita Pettik @ 2020-05-07 14:16 ` Konstantin Osipov 2020-05-07 21:47 ` Vladislav Shpilevoy 2020-05-07 21:47 ` Vladislav Shpilevoy 1 sibling, 1 reply; 25+ messages in thread From: Konstantin Osipov @ 2020-05-07 14:16 UTC (permalink / raw) To: Nikita Pettik; +Cc: tarantool-patches, Vladislav Shpilevoy * Nikita Pettik <korablev@tarantool.org> [20/05/07 16:05]: > > > > Reference counter looks like not a good information channel. > > Could you use run->fd to check whether the run was really recovered? > > vy_run_recover() leaves it -1, when fails. > > > > Otherwise this won't work the second when we will ref the run anywhere > > else. > > Firstly, lsm at this point is not restored, ergo it is not functional > and run can't be refed somewehere else - it's life span is clearly > defined. Secondly, the problem is not in the last run (which failed to > recover) but in those which are already recovered at the moment. > Recovered runs feature valid fds. Finally, slice recover may fail > not only in vy_run_recover(), but also due to oom, broken vylog etc. > All these scenarios lead to the same situation. It should be partially restored in case of force_recovery. It's another bug force_recovery is not respected, I've been sending a fix to the list a few months ago. -- Konstantin Osipov, Moscow, Russia ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails 2020-05-07 14:16 ` Konstantin Osipov @ 2020-05-07 21:47 ` Vladislav Shpilevoy 2020-05-07 22:37 ` Nikita Pettik 0 siblings, 1 reply; 25+ messages in thread From: Vladislav Shpilevoy @ 2020-05-07 21:47 UTC (permalink / raw) To: Konstantin Osipov, Nikita Pettik, tarantool-patches, alyapunov On 07/05/2020 16:16, Konstantin Osipov wrote: > * Nikita Pettik <korablev@tarantool.org> [20/05/07 16:05]: >>> >>> Reference counter looks like not a good information channel. >>> Could you use run->fd to check whether the run was really recovered? >>> vy_run_recover() leaves it -1, when fails. >>> >>> Otherwise this won't work the second when we will ref the run anywhere >>> else. >> >> Firstly, lsm at this point is not restored, ergo it is not functional >> and run can't be refed somewehere else - it's life span is clearly >> defined. Secondly, the problem is not in the last run (which failed to >> recover) but in those which are already recovered at the moment. >> Recovered runs feature valid fds. Finally, slice recover may fail >> not only in vy_run_recover(), but also due to oom, broken vylog etc. >> All these scenarios lead to the same situation. > > It should be partially restored in case of force_recovery. It's > another bug force_recovery is not respected, I've been sending a > fix to the list a few months ago. Is there a test case and an issue on that? Nikita, could you please find it/file it/confirm it/reject it? ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails 2020-05-07 21:47 ` Vladislav Shpilevoy @ 2020-05-07 22:37 ` Nikita Pettik 0 siblings, 0 replies; 25+ messages in thread From: Nikita Pettik @ 2020-05-07 22:37 UTC (permalink / raw) To: Vladislav Shpilevoy; +Cc: tarantool-patches On 07 May 23:47, Vladislav Shpilevoy wrote: > On 07/05/2020 16:16, Konstantin Osipov wrote: > > * Nikita Pettik <korablev@tarantool.org> [20/05/07 16:05]: > >>> > >>> Reference counter looks like not a good information channel. > >>> Could you use run->fd to check whether the run was really recovered? > >>> vy_run_recover() leaves it -1, when fails. > >>> > >>> Otherwise this won't work the second when we will ref the run anywhere > >>> else. > >> > >> Firstly, lsm at this point is not restored, ergo it is not functional > >> and run can't be refed somewehere else - it's life span is clearly > >> defined. Secondly, the problem is not in the last run (which failed to > >> recover) but in those which are already recovered at the moment. > >> Recovered runs feature valid fds. Finally, slice recover may fail > >> not only in vy_run_recover(), but also due to oom, broken vylog etc. > >> All these scenarios lead to the same situation. > > > > It should be partially restored in case of force_recovery. It's > > another bug force_recovery is not respected, I've been sending a > > fix to the list a few months ago. > > Is there a test case and an issue on that? Nikita, could you please > find it/file it/confirm it/reject it? I've found this issue: https://github.com/tarantool/tarantool/issues/4474 ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails 2020-05-07 13:02 ` Nikita Pettik 2020-05-07 14:16 ` Konstantin Osipov @ 2020-05-07 21:47 ` Vladislav Shpilevoy 2020-05-07 22:36 ` Nikita Pettik 1 sibling, 1 reply; 25+ messages in thread From: Vladislav Shpilevoy @ 2020-05-07 21:47 UTC (permalink / raw) To: Nikita Pettik; +Cc: tarantool-patches Thanks for the explanation and the new commit message! >>> diff --git a/src/box/vy_lsm.c b/src/box/vy_lsm.c >>> index 3d3f41b7a..81b011c69 100644 >>> --- a/src/box/vy_lsm.c >>> +++ b/src/box/vy_lsm.c >>> @@ -604,9 +604,17 @@ vy_lsm_recover(struct vy_lsm *lsm, struct vy_recovery *recovery, >>> * of each recovered run. We need to drop the extra >>> * references once we are done. >>> */ >>> - struct vy_run *run; >>> - rlist_foreach_entry(run, &lsm->runs, in_lsm) { >>> - assert(run->refs > 1); >>> + struct vy_run *run, *next_run; >>> + rlist_foreach_entry_safe(run, &lsm->runs, in_lsm, next_run) { >>> + /* >>> + * In case vy_lsm_recover_range() failed, slices >>> + * are already deleted and runs are unreffed. So >>> + * we have nothing to do but finish run clean-up. >>> + */ >>> + if (run->refs == 1) { >> >> Reference counter looks like not a good information channel. >> Could you use run->fd to check whether the run was really recovered? >> vy_run_recover() leaves it -1, when fails. >> >> Otherwise this won't work the second when we will ref the run anywhere >> else. > > Firstly, lsm at this point is not restored, ergo it is not functional > and run can't be refed somewehere else - it's life span is clearly > defined. Secondly, the problem is not in the last run (which failed to > recover) but in those which are already recovered at the moment. > Recovered runs feature valid fds. Finally, slice recover may fail > not only in vy_run_recover(), but also due to oom, broken vylog etc. > All these scenarios lead to the same situation. Yeah, fair. Then what about run->slice_count? If it is zero, then it is not kept by any slice. So we can look at slice_count == 0 and assert ref == 1. Or look at ref == 1, and assert slice_count == 0. Whatever. Will that work? ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails 2020-05-07 21:47 ` Vladislav Shpilevoy @ 2020-05-07 22:36 ` Nikita Pettik 2020-05-10 19:59 ` Vladislav Shpilevoy 0 siblings, 1 reply; 25+ messages in thread From: Nikita Pettik @ 2020-05-07 22:36 UTC (permalink / raw) To: Vladislav Shpilevoy; +Cc: tarantool-patches On 07 May 23:47, Vladislav Shpilevoy wrote: > Thanks for the explanation and the new commit message! > > >>> diff --git a/src/box/vy_lsm.c b/src/box/vy_lsm.c > >>> index 3d3f41b7a..81b011c69 100644 > >>> --- a/src/box/vy_lsm.c > >>> +++ b/src/box/vy_lsm.c > >>> @@ -604,9 +604,17 @@ vy_lsm_recover(struct vy_lsm *lsm, struct vy_recovery *recovery, > >>> * of each recovered run. We need to drop the extra > >>> * references once we are done. > >>> */ > >>> - struct vy_run *run; > >>> - rlist_foreach_entry(run, &lsm->runs, in_lsm) { > >>> - assert(run->refs > 1); > >>> + struct vy_run *run, *next_run; > >>> + rlist_foreach_entry_safe(run, &lsm->runs, in_lsm, next_run) { > >>> + /* > >>> + * In case vy_lsm_recover_range() failed, slices > >>> + * are already deleted and runs are unreffed. So > >>> + * we have nothing to do but finish run clean-up. > >>> + */ > >>> + if (run->refs == 1) { > >> > >> Reference counter looks like not a good information channel. > >> Could you use run->fd to check whether the run was really recovered? > >> vy_run_recover() leaves it -1, when fails. > >> > >> Otherwise this won't work the second when we will ref the run anywhere > >> else. > > > > Firstly, lsm at this point is not restored, ergo it is not functional > > and run can't be refed somewehere else - it's life span is clearly > > defined. Secondly, the problem is not in the last run (which failed to > > recover) but in those which are already recovered at the moment. > > Recovered runs feature valid fds. Finally, slice recover may fail > > not only in vy_run_recover(), but also due to oom, broken vylog etc. > > All these scenarios lead to the same situation. > > Yeah, fair. Then what about run->slice_count? If it is zero, then it > is not kept by any slice. So we can look at slice_count == 0 and > assert ref == 1. Or look at ref == 1, and assert slice_count == 0. > Whatever. Will that work? diff --git a/src/box/vy_lsm.c b/src/box/vy_lsm.c index 7755f04f0..005dde3b2 100644 --- a/src/box/vy_lsm.c +++ b/src/box/vy_lsm.c @@ -613,6 +613,7 @@ vy_lsm_recover(struct vy_lsm *lsm, struct vy_recovery *recovery, */ if (run->refs == 1) { assert(rc != 0); + assert(run->slice_count == 0); vy_lsm_remove_run(lsm, run); } vy_run_unref(run); ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails 2020-05-07 22:36 ` Nikita Pettik @ 2020-05-10 19:59 ` Vladislav Shpilevoy 2020-05-12 1:16 ` Nikita Pettik 0 siblings, 1 reply; 25+ messages in thread From: Vladislav Shpilevoy @ 2020-05-10 19:59 UTC (permalink / raw) To: Nikita Pettik; +Cc: tarantool-patches Hi! Thanks for the fixes! See 1 comment below. > diff --git a/test/vinyl/gh-4805-open-run-err-recovery.result b/test/vinyl/gh-4805-open-run-err-recovery.result > new file mode 100644 > index 000000000..31e0e7857 > --- /dev/null > +++ b/test/vinyl/gh-4805-open-run-err-recovery.result > @@ -0,0 +1,101 @@ > +-- test-run result file version 2 > +fiber = require('fiber') > + | --- > + | ... > +test_run = require('test_run').new() > + | --- > + | ... > + > +test_run:cmd('create server err_recovery with script = "vinyl/errinj_recovery.lua"') > + | --- > + | - true > + | ... > +test_run:cmd('start server err_recovery') > + | --- > + | - true > + | ... > +test_run:cmd('switch err_recovery') > + | --- > + | - true > + | ... > + > +s = box.schema.space.create('test', {engine = 'vinyl'}) > + | --- > + | ... > +_ = s:create_index('pk', {run_count_per_level = 100, page_size = 128, range_size = 1024}) > + | --- > + | ... > + > +digest = require('digest') > + | --- > + | ... > +test_run:cmd("setopt delimiter ';'") > + | --- > + | - true > + | ... > +function dump(big) > + local step = big and 1 or 5 > + for i = 1, 20, step do > + s:replace{i, digest.urandom(1000)} > + end > + box.snapshot() > +end; > + | --- > + | ... > +test_run:cmd("setopt delimiter ''"); > + | --- > + | - true > + | ... > + > +dump(true) > + | --- > + | ... > +dump() > + | --- > + | ... > +dump() > + | --- > + | ... > + > +test_run:cmd('switch default') > + | --- > + | - true > + | ... > +test_run:cmd('stop server err_recovery') > + | --- > + | - true > + | ... > +test_run:cmd('start server err_recovery with crash_expected=True') > + | --- > + | - false > + | ... > + > +fio = require('fio') > + | --- > + | ... > +fh = fio.open(fio.pathjoin(fio.cwd(), 'errinj_recovery.log'), {'O_RDONLY'}) > + | --- > + | ... > +size = fh:seek(0, 'SEEK_END') > + | --- > + | ... > +fh:seek(-256, 'SEEK_END') ~= nil > + | --- > + | - true > + | ... > +line = fh:read(256) > + | --- > + | ... > +fh:close() > + | --- > + | - true > + | ... > +string.match(line, 'failed to open') ~= nil Why can't you use test_run:grep_log()? It supports searching in logs of an already crashed instance too. See its parameters in test_run.lua. Looks like it can solve your task. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails 2020-05-10 19:59 ` Vladislav Shpilevoy @ 2020-05-12 1:16 ` Nikita Pettik 0 siblings, 0 replies; 25+ messages in thread From: Nikita Pettik @ 2020-05-12 1:16 UTC (permalink / raw) To: Vladislav Shpilevoy; +Cc: tarantool-patches On 10 May 21:59, Vladislav Shpilevoy wrote: > Hi! Thanks for the fixes! > > See 1 comment below. > > > +line = fh:read(256) > > + | --- > > + | ... > > +fh:close() > > + | --- > > + | - true > > + | ... > > +string.match(line, 'failed to open') ~= nil > > Why can't you use test_run:grep_log()? It supports > searching in logs of an already crashed instance too. > See its parameters in test_run.lua. Looks like it can > solve your task. > Indeed. Here's diff: diff --git a/test/vinyl/gh-4805-open-run-err-recovery.test.lua b/test/vinyl/gh-4805-open-run-err-recovery.test.lua index 8b5895d41..0b97f09da 100644 --- a/test/vinyl/gh-4805-open-run-err-recovery.test.lua +++ b/test/vinyl/gh-4805-open-run-err-recovery.test.lua @@ -27,12 +27,8 @@ test_run:cmd('switch default') test_run:cmd('stop server err_recovery') test_run:cmd('start server err_recovery with crash_expected=True') -fio = require('fio') -fh = fio.open(fio.pathjoin(fio.cwd(), 'errinj_recovery.log'), {'O_RDONLY'}) -size = fh:seek(0, 'SEEK_END') -fh:seek(-256, 'SEEK_END') ~= nil -line = fh:read(256) -fh:close() -string.match(line, 'failed to open') ~= nil +opts = {} +opts.filename = 'errinj_recovery.log' +test_run:grep_log('err_recovery', 'failed to open', 1000, opts) ~= nil test_run:cmd('delete server err_recovery') ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery 2020-04-30 19:27 [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery Nikita Pettik 2020-04-30 19:27 ` [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection Nikita Pettik 2020-04-30 19:27 ` [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails Nikita Pettik @ 2020-05-03 19:20 ` Vladislav Shpilevoy 2020-05-07 14:11 ` Nikita Pettik 2020-05-12 20:53 ` Vladislav Shpilevoy 3 siblings, 1 reply; 25+ messages in thread From: Vladislav Shpilevoy @ 2020-05-03 19:20 UTC (permalink / raw) To: Nikita Pettik, tarantool-patches Why do you base your patches on top of 1.10? Doesn't the bug exist on master? On 30/04/2020 21:27, Nikita Pettik wrote: > Branch: https://github.com/tarantool/tarantool/commits/np/gh-4805-too-many-fds > Issue: https://github.com/tarantool/tarantool/issues/4805 > > First patch adds simple macro which allows error injection to be delayed. > It also can be used in this series: > https://lists.tarantool.org/pipermail/tarantool-patches/2020-April/016367.html > > Nikita Pettik (2): > errinj: introduce delayed injection > vinyl: drop wasted runs in case range recovery fails > > src/box/vy_lsm.c | 14 ++- > src/box/vy_run.c | 4 + > src/errinj.h | 10 ++ > test/box/errinj.result | 1 + > test/vinyl/errinj_recovery.lua | 10 ++ > .../gh-4805-open-run-err-recovery.result | 101 ++++++++++++++++++ > .../gh-4805-open-run-err-recovery.test.lua | 38 +++++++ > test/vinyl/suite.ini | 2 +- > 8 files changed, 176 insertions(+), 4 deletions(-) > create mode 100644 test/vinyl/errinj_recovery.lua > create mode 100644 test/vinyl/gh-4805-open-run-err-recovery.result > create mode 100644 test/vinyl/gh-4805-open-run-err-recovery.test.lua > ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery 2020-05-03 19:20 ` [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery Vladislav Shpilevoy @ 2020-05-07 14:11 ` Nikita Pettik 0 siblings, 0 replies; 25+ messages in thread From: Nikita Pettik @ 2020-05-07 14:11 UTC (permalink / raw) To: Vladislav Shpilevoy; +Cc: tarantool-patches On 03 May 21:20, Vladislav Shpilevoy wrote: > Why do you base your patches on top of 1.10? Doesn't the > bug exist on master? Yep, master is affected as well. Originally I found it on 1.10 so firstly prepared patch for 1.10. There's no big difference in lsm recovery code, so patch can be backported with ease (from master to 1.10). Will push to master firstly, when patch-set is ready-to-push. Anyway, thanks for noting that. > On 30/04/2020 21:27, Nikita Pettik wrote: > > Branch: https://github.com/tarantool/tarantool/commits/np/gh-4805-too-many-fds > > Issue: https://github.com/tarantool/tarantool/issues/4805 > > > > First patch adds simple macro which allows error injection to be delayed. > > It also can be used in this series: > > https://lists.tarantool.org/pipermail/tarantool-patches/2020-April/016367.html > > > > Nikita Pettik (2): > > errinj: introduce delayed injection > > vinyl: drop wasted runs in case range recovery fails > > > > src/box/vy_lsm.c | 14 ++- > > src/box/vy_run.c | 4 + > > src/errinj.h | 10 ++ > > test/box/errinj.result | 1 + > > test/vinyl/errinj_recovery.lua | 10 ++ > > .../gh-4805-open-run-err-recovery.result | 101 ++++++++++++++++++ > > .../gh-4805-open-run-err-recovery.test.lua | 38 +++++++ > > test/vinyl/suite.ini | 2 +- > > 8 files changed, 176 insertions(+), 4 deletions(-) > > create mode 100644 test/vinyl/errinj_recovery.lua > > create mode 100644 test/vinyl/gh-4805-open-run-err-recovery.result > > create mode 100644 test/vinyl/gh-4805-open-run-err-recovery.test.lua > > ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery 2020-04-30 19:27 [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery Nikita Pettik ` (2 preceding siblings ...) 2020-05-03 19:20 ` [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery Vladislav Shpilevoy @ 2020-05-12 20:53 ` Vladislav Shpilevoy 2020-05-12 20:56 ` Vladislav Shpilevoy 3 siblings, 1 reply; 25+ messages in thread From: Vladislav Shpilevoy @ 2020-05-12 20:53 UTC (permalink / raw) To: Nikita Pettik, tarantool-patches Hi! Thanks for the patchset and the fixes! LGTM. On 30/04/2020 21:27, Nikita Pettik wrote: > Branch: https://github.com/tarantool/tarantool/commits/np/gh-4805-too-many-fds > Issue: https://github.com/tarantool/tarantool/issues/4805 > > First patch adds simple macro which allows error injection to be delayed. > It also can be used in this series: > https://lists.tarantool.org/pipermail/tarantool-patches/2020-April/016367.html > > Nikita Pettik (2): > errinj: introduce delayed injection > vinyl: drop wasted runs in case range recovery fails > > src/box/vy_lsm.c | 14 ++- > src/box/vy_run.c | 4 + > src/errinj.h | 10 ++ > test/box/errinj.result | 1 + > test/vinyl/errinj_recovery.lua | 10 ++ > .../gh-4805-open-run-err-recovery.result | 101 ++++++++++++++++++ > .../gh-4805-open-run-err-recovery.test.lua | 38 +++++++ > test/vinyl/suite.ini | 2 +- > 8 files changed, 176 insertions(+), 4 deletions(-) > create mode 100644 test/vinyl/errinj_recovery.lua > create mode 100644 test/vinyl/gh-4805-open-run-err-recovery.result > create mode 100644 test/vinyl/gh-4805-open-run-err-recovery.test.lua > ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery 2020-05-12 20:53 ` Vladislav Shpilevoy @ 2020-05-12 20:56 ` Vladislav Shpilevoy 2020-05-12 22:45 ` Nikita Pettik 0 siblings, 1 reply; 25+ messages in thread From: Vladislav Shpilevoy @ 2020-05-12 20:56 UTC (permalink / raw) To: Nikita Pettik, tarantool-patches Ah, yes, almost forgot: Reviewed-by: Vladislav Shpilevoy <vshpilevoi@mail.ru> 😎 ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery 2020-05-12 20:56 ` Vladislav Shpilevoy @ 2020-05-12 22:45 ` Nikita Pettik 2020-05-13 20:19 ` Vladislav Shpilevoy 0 siblings, 1 reply; 25+ messages in thread From: Nikita Pettik @ 2020-05-12 22:45 UTC (permalink / raw) To: Vladislav Shpilevoy; +Cc: tarantool-patches On 12 May 22:56, Vladislav Shpilevoy wrote: > Ah, yes, almost forgot: > > Reviewed-by: Vladislav Shpilevoy <vshpilevoi@mail.ru> 😎 Is this official now? Is SOP already updated? Pushed to master; backported to 2.4, 2.3 and 1.10. Changelogs are updated correspondingly. Branch is dropped. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery 2020-05-12 22:45 ` Nikita Pettik @ 2020-05-13 20:19 ` Vladislav Shpilevoy 0 siblings, 0 replies; 25+ messages in thread From: Vladislav Shpilevoy @ 2020-05-13 20:19 UTC (permalink / raw) To: Nikita Pettik; +Cc: tarantool-patches On 13/05/2020 00:45, Nikita Pettik wrote: > On 12 May 22:56, Vladislav Shpilevoy wrote: >> Ah, yes, almost forgot: >> >> Reviewed-by: Vladislav Shpilevoy <vshpilevoi@mail.ru> 😎 > > Is this official now? Is SOP already updated? > > Pushed to master; backported to 2.4, 2.3 and 1.10. Changelogs are updated > correspondingly. Branch is dropped. It is voluntary, from what I understood. I saw some people already adding it. You can omit this if you want. ^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2020-05-13 20:19 UTC | newest] Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-04-30 19:27 [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery Nikita Pettik 2020-04-30 19:27 ` [Tarantool-patches] [PATCH 1/2] errinj: introduce delayed injection Nikita Pettik 2020-04-30 20:15 ` Konstantin Osipov 2020-04-30 20:55 ` Nikita Pettik 2020-05-01 19:15 ` Konstantin Osipov 2020-05-03 19:20 ` Vladislav Shpilevoy 2020-05-07 13:50 ` Nikita Pettik 2020-05-07 21:47 ` Vladislav Shpilevoy 2020-05-07 22:41 ` Nikita Pettik 2020-04-30 19:27 ` [Tarantool-patches] [PATCH 2/2] vinyl: drop wasted runs in case range recovery fails Nikita Pettik 2020-05-03 19:21 ` Vladislav Shpilevoy 2020-05-07 13:02 ` Nikita Pettik 2020-05-07 14:16 ` Konstantin Osipov 2020-05-07 21:47 ` Vladislav Shpilevoy 2020-05-07 22:37 ` Nikita Pettik 2020-05-07 21:47 ` Vladislav Shpilevoy 2020-05-07 22:36 ` Nikita Pettik 2020-05-10 19:59 ` Vladislav Shpilevoy 2020-05-12 1:16 ` Nikita Pettik 2020-05-03 19:20 ` [Tarantool-patches] [PATCH 0/2] Fix crash in case of lack of FDs during recovery Vladislav Shpilevoy 2020-05-07 14:11 ` Nikita Pettik 2020-05-12 20:53 ` Vladislav Shpilevoy 2020-05-12 20:56 ` Vladislav Shpilevoy 2020-05-12 22:45 ` Nikita Pettik 2020-05-13 20:19 ` Vladislav Shpilevoy
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox