-
Notifications
You must be signed in to change notification settings - Fork 379
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
Stacked diagnostics area in fiber #1148
Comments
refactoring, rolling feature => 1.7.3 |
I comment here about why at some time Tarantool should support warnings with SQL.
|
Refactor iproto_reply_error and iproto_write_error with a new mpstream-based helper mpstream_iproto_encode_error that encodes error object for iproto protocol on a given stream object. Previously each routine implemented an own error encoding, but with the increasing complexity of encode operation with following patches we need a uniform way to do it. The iproto_write_error routine starts using region location to use region-based mpstream. It is not a problem itself, because errors reporting is not really performance-critical path. Needed for #1148
This patch introduces support of stacked errors in IProto protocol and in net.box module. @TarantoolBot document Title: Stacked error diagnostic area Starting from now errors can be organized into lists. To achieve this Lua table representing error object is extended with .prev field and e:set_prev(err) method. .prev field returns previous error if any exist. e:set_prev(err) method expects err to be error object or nil and sets err as previous error of e. For instance: e1 = box.error.new({code = 111, reason = "cause"}) e2 = box.error.new({code = 111, reason = "cause of cause"}) e1:set_prev(e2) assert(e1.prev == e2) -- true Cycles are not allowed for error lists: e2:set_prev(e1) - error: 'builtin/error.lua: Cycles are not allowed' Nil is valid input to :set_prev() method: e1:set_prev(nil) assert(e1.prev == nil) -- true Note that error can be 'previous' only to the one error at once: e1:set_prev(e2) e3:set_prev(e2) assert(e1.prev == nil) -- true assert(e3.prev == e2) -- true Setting previous error does not erase its own previous members: -- e1 -> e2 -> e3 -> e4 e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e2:set_prev(e5) -- Now there are two lists: e1->e2->e5 and e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == e5) -- true assert(e3.prev == e4) -- true Alternatively: e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e5:set_prev(e3) -- Now there are two lists: e1->e2 and e5->e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == nil) -- true assert(e5.prev == e3) -- true assert(e3.prev == e4) -- true Stacked diagnostics is also supported by IProto protocol. Now responses containing errors always (even if there's only one error to be returned) include new IProto key: IPROTO_ERROR_STACK (0x51). So, body corresponding to error response now looks like: MAP{IPROTO_ERROR : string, IPROTO_ERROR_STACK : ARRAY[MAP{ERROR_CODE : uint, ERROR_MESSAGE : string}, MAP{...}, ...]} where IPROTO_ERROR is 0x31 key, IPROTO_ERROR_STACK is 0x51, ERROR_CODE is 0x01 and ERROR_MESSAGE is 0x02. Instances of older versions (without support of stacked errors in protocol) simply ignore unknown keys and still rely only on IPROTO_ERROR key. Closes #1148
To achieve this let's refactor luaT_error_create() to return error object instead of setting it via box_error_set(). luaT_error_create() is used both to handle box.error() and box.error.new() invocations, and box.error() is still expected to set error to diagnostic area. So, luaT_error_call() which implements box.error() processing at the end calls diag_set_error(). It is worth mentioning that net.box module relied on the fact that box.error.new() set error to diagnostic area: otherwise request errors don't get to diagnostic area on client side. Needed for #1148 Closes #4778 @TarantoolBot document Title: Don't promote error created via box.error.new to diagnostic area Now box.error.new() only creates error object, but doesn't set it to Tarantool's diagnostic area: ``` box.error.clear() e = box.error.new({code = 111, reason = "cause"}) assert(box.error.last() == nil) --- - true ... ``` To set error in diagnostic area explicitly box.error.set() has been introduced. It accepts error object which is set as last system error (i.e. becomes available via box.error.last()). Finally, box.error.new() does not longer accept error object as an argument (this was undocumented feature). Note that patch does not affect box.error(), which still pushes error to diagnostic area. This fact is reflected in docs: ''' Emulate a request error, with text based on one of the pre-defined Tarantool errors... '''
In terms of implementation, now struct error objects can be organized into double-linked lists. To achieve this pointers to the next and previous elements (cause and effect correspondingly) have been added to struct error. It is worth mentioning that already existing rlist and stailq list implementations are not suitable: rlist is cycled list, as a result it is impossible to start iteration over the list from random list entry and finish it at the logical end of the list; stailq is single-linked list leaving no possibility to remove elements from the middle of the list. As a part of C interface, box_error_add() has been introduced. In contrast to box_error_set() it does not replace last raised error, but instead it adds error to the list of diagnostic errors having already been set. If error is to be deleted (its reference counter hits 0 value) it is unlinked from the list it belongs to and destroyed. Meanwhile, error destruction leads to decrement of reference counter of its previous error and so on. To organize errors into lists in Lua, table representing error object in Lua now has .prev field (corresponding to 'previous' error) and method :set_prev(e). The latter accepts error object (i.e. created via box.error.new() or box.error.last()) and nil value. Both field .prev and :set_prev() method are implemented as ffi functions. Also note that cycles are not allowed while organizing errors into lists: e1 -> e2 -> e3; e3:set_prev(e1) -- would lead to error. Part of #1148
Since we've introduced stacked diagnostic in previous commit, let's use it in the code implementing functional indexes. Part of #1148
Refactor iproto_reply_error and iproto_write_error with a new mpstream-based helper mpstream_iproto_encode_error that encodes error object for iproto protocol on a given stream object. Previously each routine implemented an own error encoding, but with the increasing complexity of encode operation with following patches we need a uniform way to do it. The iproto_write_error routine starts using region location to use region-based mpstream. It is not a problem itself, because errors reporting is not really performance-critical path. Needed for #1148
This patch introduces support of stacked errors in IProto protocol and in net.box module. Closes #1148 @TarantoolBot document Title: Stacked error diagnostic area Starting from now errors can be organized into lists. To achieve this Lua table representing error object is extended with .prev field and e:set_prev(err) method. .prev field returns previous error if any exist. e:set_prev(err) method expects err to be error object or nil and sets err as previous error of e. For instance: ``` e1 = box.error.new({code = 111, reason = "cause"}) e2 = box.error.new({code = 111, reason = "cause of cause"}) e1:set_prev(e2) assert(e1.prev == e2) -- true ``` Cycles are not allowed for error lists: ``` e2:set_prev(e1) - error: 'builtin/error.lua: Cycles are not allowed' ``` Nil is valid input to :set_prev() method: ``` e1:set_prev(nil) assert(e1.prev == nil) -- true ``` Note that error can be 'previous' only to the one error at once: ``` e1:set_prev(e2) e3:set_prev(e2) assert(e1.prev == nil) -- true assert(e3.prev == e2) -- true ``` Setting previous error does not erase its own previous members: ``` -- e1 -> e2 -> e3 -> e4 e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e2:set_prev(e5) -- Now there are two lists: e1->e2->e5 and e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == e5) -- true assert(e3.prev == e4) -- true ``` Alternatively: ``` e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e5:set_prev(e3) -- Now there are two lists: e1->e2 and e5->e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == nil) -- true assert(e5.prev == e3) -- true assert(e3.prev == e4) -- true `` Stacked diagnostics is also supported by IProto protocol. Now responses containing errors always (even if there's only one error to be returned) include new IProto key: IPROTO_ERROR_STACK (0x51). So, body corresponding to error response now looks like: ``` MAP{IPROTO_ERROR : string, IPROTO_ERROR_STACK : ARRAY[MAP{ERROR_CODE : uint, ERROR_MESSAGE : string}, MAP{...}, ...]} ``` where IPROTO_ERROR is 0x31 key, IPROTO_ERROR_STACK is 0x51, ERROR_CODE is 0x01 and ERROR_MESSAGE is 0x02. Instances of older versions (without support of stacked errors in protocol) simply ignore unknown keys and still rely only on IPROTO_ERROR key.
This patch introduces support of stacked errors in IProto protocol and in net.box module. Closes #1148 @TarantoolBot document Title: Stacked error diagnostic area Starting from now errors can be organized into lists. To achieve this Lua table representing error object is extended with .prev field and e:set_prev(err) method. .prev field returns previous error if any exist. e:set_prev(err) method expects err to be error object or nil and sets err as previous error of e. For instance: ``` e1 = box.error.new({code = 111, reason = "cause"}) e2 = box.error.new({code = 111, reason = "cause of cause"}) e1:set_prev(e2) assert(e1.prev == e2) -- true ``` Cycles are not allowed for error lists: ``` e2:set_prev(e1) - error: 'builtin/error.lua: Cycles are not allowed' ``` Nil is valid input to :set_prev() method: ``` e1:set_prev(nil) assert(e1.prev == nil) -- true ``` Note that error can be 'previous' only to the one error at once: ``` e1:set_prev(e2) e3:set_prev(e2) assert(e1.prev == nil) -- true assert(e3.prev == e2) -- true ``` Setting previous error does not erase its own previous members: ``` -- e1 -> e2 -> e3 -> e4 e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e2:set_prev(e5) -- Now there are two lists: e1->e2->e5 and e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == e5) -- true assert(e3.prev == e4) -- true ``` Alternatively: ``` e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e5:set_prev(e3) -- Now there are two lists: e1->e2 and e5->e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == nil) -- true assert(e5.prev == e3) -- true assert(e3.prev == e4) -- true `` Stacked diagnostics is also supported by IProto protocol. Now responses containing errors always (even if there's only one error to be returned) include new IProto key: IPROTO_ERROR_STACK (0x51). So, body corresponding to error response now looks like: ``` MAP{IPROTO_ERROR : string, IPROTO_ERROR_STACK : ARRAY[MAP{ERROR_CODE : uint, ERROR_MESSAGE : string}, MAP{...}, ...]} ``` where IPROTO_ERROR is 0x31 key, IPROTO_ERROR_STACK is 0x51, ERROR_CODE is 0x01 and ERROR_MESSAGE is 0x02. Instances of older versions (without support of stacked errors in protocol) simply ignore unknown keys and still rely only on IPROTO_ERROR key.
In terms of implementation, now struct error objects can be organized into double-linked lists. To achieve this pointers to the next and previous elements (cause and effect correspondingly) have been added to struct error. It is worth mentioning that already existing rlist and stailq list implementations are not suitable: rlist is cycled list, as a result it is impossible to start iteration over the list from random list entry and finish it at the logical end of the list; stailq is single-linked list leaving no possibility to remove elements from the middle of the list. As a part of C interface, box_error_add() has been introduced. In contrast to box_error_set() it does not replace last raised error, but instead it adds error to the list of diagnostic errors having already been set. If error is to be deleted (its reference counter hits 0 value) it is unlinked from the list it belongs to and destroyed. Meanwhile, error destruction leads to decrement of reference counter of its previous error and so on. To organize errors into lists in Lua, table representing error object in Lua now has .prev field (corresponding to 'previous' error) and method :set_prev(e). The latter accepts error object (i.e. created via box.error.new() or box.error.last()) and nil value. Both field .prev and :set_prev() method are implemented as ffi functions. Also note that cycles are not allowed while organizing errors into lists: e1 -> e2 -> e3; e3:set_prev(e1) -- would lead to error. Part of #1148
Since we've introduced stacked diagnostic in previous commit, let's use it in the code implementing functional indexes. Part of #1148
Refactor iproto_reply_error and iproto_write_error with a new mpstream-based helper mpstream_iproto_encode_error that encodes error object for iproto protocol on a given stream object. Previously each routine implemented an own error encoding, but with the increasing complexity of encode operation with following patches we need a uniform way to do it. The iproto_write_error routine starts using region location to use region-based mpstream. It is not a problem itself, because errors reporting is not really performance-critical path. Needed for #1148
This patch introduces support of stacked errors in IProto protocol and in net.box module. Closes #1148 @TarantoolBot document Title: Stacked error diagnostic area Starting from now errors can be organized into lists. To achieve this Lua table representing error object is extended with .prev field and e:set_prev(err) method. .prev field returns previous error if any exist. e:set_prev(err) method expects err to be error object or nil and sets err as previous error of e. For instance: ``` e1 = box.error.new({code = 111, reason = "cause"}) e2 = box.error.new({code = 111, reason = "cause of cause"}) e1:set_prev(e2) assert(e1.prev == e2) -- true ``` Cycles are not allowed for error lists: ``` e2:set_prev(e1) - error: 'builtin/error.lua: Cycles are not allowed' ``` Nil is valid input to :set_prev() method: ``` e1:set_prev(nil) assert(e1.prev == nil) -- true ``` Note that error can be 'previous' only to the one error at once: ``` e1:set_prev(e2) e3:set_prev(e2) assert(e1.prev == nil) -- true assert(e3.prev == e2) -- true ``` Setting previous error does not erase its own previous members: ``` -- e1 -> e2 -> e3 -> e4 e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e2:set_prev(e5) -- Now there are two lists: e1->e2->e5 and e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == e5) -- true assert(e3.prev == e4) -- true ``` Alternatively: ``` e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e5:set_prev(e3) -- Now there are two lists: e1->e2 and e5->e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == nil) -- true assert(e5.prev == e3) -- true assert(e3.prev == e4) -- true `` Stacked diagnostics is also supported by IProto protocol. Now responses containing errors always (even if there's only one error to be returned) include new IProto key: IPROTO_ERROR_STACK (0x51). So, body corresponding to error response now looks like: ``` MAP{IPROTO_ERROR : string, IPROTO_ERROR_STACK : ARRAY[MAP{ERROR_CODE : uint, ERROR_MESSAGE : string}, MAP{...}, ...]} ``` where IPROTO_ERROR is 0x31 key, IPROTO_ERROR_STACK is 0x51, ERROR_CODE is 0x01 and ERROR_MESSAGE is 0x02. Instances of older versions (without support of stacked errors in protocol) simply ignore unknown keys and still rely only on IPROTO_ERROR key.
Since we've introduced stacked diagnostic in previous commit, let's use it in the code implementing functional indexes. Part of #1148
Refactor iproto_reply_error and iproto_write_error with a new mpstream-based helper mpstream_iproto_encode_error that encodes error object for iproto protocol on a given stream object. Previously each routine implemented an own error encoding, but with the increasing complexity of encode operation with following patches we need a uniform way to do it. The iproto_write_error routine starts using region location to use region-based mpstream. It is not a problem itself, because errors reporting is not really performance-critical path. Needed for #1148
This patch introduces support of stacked errors in IProto protocol and in net.box module. Closes #1148 @TarantoolBot document Title: Stacked error diagnostic area Starting from now errors can be organized into lists. To achieve this Lua table representing error object is extended with .prev field and e:set_prev(err) method. .prev field returns previous error if any exist. e:set_prev(err) method expects err to be error object or nil and sets err as previous error of e. For instance: ``` e1 = box.error.new({code = 111, reason = "cause"}) e2 = box.error.new({code = 111, reason = "cause of cause"}) e1:set_prev(e2) assert(e1.prev == e2) -- true ``` Cycles are not allowed for error lists: ``` e2:set_prev(e1) - error: 'builtin/error.lua: Cycles are not allowed' ``` Nil is valid input to :set_prev() method: ``` e1:set_prev(nil) assert(e1.prev == nil) -- true ``` Note that error can be 'previous' only to the one error at once: ``` e1:set_prev(e2) e3:set_prev(e2) assert(e1.prev == nil) -- true assert(e3.prev == e2) -- true ``` Setting previous error does not erase its own previous members: ``` -- e1 -> e2 -> e3 -> e4 e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e2:set_prev(e5) -- Now there are two lists: e1->e2->e5 and e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == e5) -- true assert(e3.prev == e4) -- true ``` Alternatively: ``` e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e5:set_prev(e3) -- Now there are two lists: e1->e2 and e5->e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == nil) -- true assert(e5.prev == e3) -- true assert(e3.prev == e4) -- true `` Stacked diagnostics is also supported by IProto protocol. Now responses containing errors always (even if there's only one error to be returned) include new IProto key: IPROTO_ERROR_STACK (0x51). So, body corresponding to error response now looks like: ``` MAP{IPROTO_ERROR : string, IPROTO_ERROR_STACK : ARRAY[MAP{ERROR_CODE : uint, ERROR_MESSAGE : string}, MAP{...}, ...]} ``` where IPROTO_ERROR is 0x31 key, IPROTO_ERROR_STACK is 0x51, ERROR_CODE is 0x01 and ERROR_MESSAGE is 0x02. Instances of older versions (without support of stacked errors in protocol) simply ignore unknown keys and still rely only on IPROTO_ERROR key.
In terms of implementation, now struct error objects can be organized into double-linked lists. To achieve this pointers to the next and previous elements (cause and effect correspondingly) have been added to struct error. It is worth mentioning that already existing rlist and stailq list implementations are not suitable: rlist is cycled list, as a result it is impossible to start iteration over the list from random list entry and finish it at the logical end of the list; stailq is single-linked list leaving no possibility to remove elements from the middle of the list. As a part of C interface, box_error_add() has been introduced. In contrast to box_error_set() it does not replace last raised error, but instead it adds error to the list of diagnostic errors having already been set. If error is to be deleted (its reference counter hits 0 value) it is unlinked from the list it belongs to and destroyed. Meanwhile, error destruction leads to decrement of reference counter of its previous error and so on. To organize errors into lists in Lua, table representing error object in Lua now has .prev field (corresponding to 'previous' error) and method :set_prev(e). The latter accepts error object (i.e. created via box.error.new() or box.error.last()) and nil value. Both field .prev and :set_prev() method are implemented as ffi functions. Also note that cycles are not allowed while organizing errors into lists: e1 -> e2 -> e3; e3:set_prev(e1) -- would lead to error. Part of #1148
Since we've introduced stacked diagnostic in previous commit, let's use it in the code implementing functional indexes. Part of #1148
Refactor iproto_reply_error and iproto_write_error with a new mpstream-based helper mpstream_iproto_encode_error that encodes error object for iproto protocol on a given stream object. Previously each routine implemented an own error encoding, but with the increasing complexity of encode operation with following patches we need a uniform way to do it. The iproto_write_error routine starts using region location to use region-based mpstream. It is not a problem itself, because errors reporting is not really performance-critical path. Needed for #1148
This patch introduces support of stacked errors in IProto protocol and in net.box module. Closes #1148 @TarantoolBot document Title: Stacked error diagnostic area Starting from now errors can be organized into lists. To achieve this Lua table representing error object is extended with .prev field and e:set_prev(err) method. .prev field returns previous error if any exist. e:set_prev(err) method expects err to be error object or nil and sets err as previous error of e. For instance: ``` e1 = box.error.new({code = 111, reason = "cause"}) e2 = box.error.new({code = 111, reason = "cause of cause"}) e1:set_prev(e2) assert(e1.prev == e2) -- true ``` Cycles are not allowed for error lists: ``` e2:set_prev(e1) - error: 'builtin/error.lua: Cycles are not allowed' ``` Nil is valid input to :set_prev() method: ``` e1:set_prev(nil) assert(e1.prev == nil) -- true ``` Note that error can be 'previous' only to the one error at once: ``` e1:set_prev(e2) e3:set_prev(e2) assert(e1.prev == nil) -- true assert(e3.prev == e2) -- true ``` Setting previous error does not erase its own previous members: ``` -- e1 -> e2 -> e3 -> e4 e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e2:set_prev(e5) -- Now there are two lists: e1->e2->e5 and e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == e5) -- true assert(e3.prev == e4) -- true ``` Alternatively: ``` e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e5:set_prev(e3) -- Now there are two lists: e1->e2 and e5->e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == nil) -- true assert(e5.prev == e3) -- true assert(e3.prev == e4) -- true `` Stacked diagnostics is also supported by IProto protocol. Now responses containing errors always (even if there's only one error to be returned) include new IProto key: IPROTO_ERROR_STACK (0x51). So, body corresponding to error response now looks like: ``` MAP{IPROTO_ERROR : string, IPROTO_ERROR_STACK : ARRAY[MAP{ERROR_CODE : uint, ERROR_MESSAGE : string}, MAP{...}, ...]} ``` where IPROTO_ERROR is 0x31 key, IPROTO_ERROR_STACK is 0x51, ERROR_CODE is 0x01 and ERROR_MESSAGE is 0x02. Instances of older versions (without support of stacked errors in protocol) simply ignore unknown keys and still rely only on IPROTO_ERROR key.
This patch introduces support of stacked errors in IProto protocol and in net.box module. Closes #1148 @TarantoolBot document Title: Stacked error diagnostic area Starting from now errors can be organized into lists. To achieve this Lua table representing error object is extended with .prev field and e:set_prev(err) method. .prev field returns previous error if any exist. e:set_prev(err) method expects err to be error object or nil and sets err as previous error of e. For instance: ``` e1 = box.error.new({code = 111, reason = "cause"}) e2 = box.error.new({code = 111, reason = "cause of cause"}) e1:set_prev(e2) assert(e1.prev == e2) -- true ``` Cycles are not allowed for error lists: ``` e2:set_prev(e1) - error: 'builtin/error.lua: Cycles are not allowed' ``` Nil is valid input to :set_prev() method: ``` e1:set_prev(nil) assert(e1.prev == nil) -- true ``` Note that error can be 'previous' only to the one error at once: ``` e1:set_prev(e2) e3:set_prev(e2) assert(e1.prev == nil) -- true assert(e3.prev == e2) -- true ``` Setting previous error does not erase its own previous members: ``` -- e1 -> e2 -> e3 -> e4 e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e2:set_prev(e5) -- Now there are two lists: e1->e2->e5 and e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == e5) -- true assert(e3.prev == e4) -- true ``` Alternatively: ``` e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e5:set_prev(e3) -- Now there are two lists: e1->e2 and e5->e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == nil) -- true assert(e5.prev == e3) -- true assert(e3.prev == e4) -- true `` Stacked diagnostics is also supported by IProto protocol. Now responses containing errors always (even if there's only one error to be returned) include new IProto key: IPROTO_ERROR_STACK (0x51). So, body corresponding to error response now looks like: ``` MAP{IPROTO_ERROR : string, IPROTO_ERROR_STACK : ARRAY[MAP{ERROR_CODE : uint, ERROR_MESSAGE : string}, MAP{...}, ...]} ``` where IPROTO_ERROR is 0x31 key, IPROTO_ERROR_STACK is 0x51, ERROR_CODE is 0x01 and ERROR_MESSAGE is 0x02. Instances of older versions (without support of stacked errors in protocol) simply ignore unknown keys and still rely only on IPROTO_ERROR key.
Before this patch, branch when getaddrinfo() returns error codes couldn't be reached on macOS, because they are greater than 0 on macOS (assumption "rc < 0" in commit ea1da04 is incorrect for macOS). Note: diag_log() in say.c was added, because otherwise it will be hid by the following diagnostic and it should be handled in a better way after #1148. Also, two diag_set() in syslog_connect_unix() was added to avoid asserts in this diag_log(). Need for #4138
In terms of implementation, now struct error objects can be organized into double-linked lists. To achieve this pointers to the next and previous elements (cause and effect correspondingly) have been added to struct error. It is worth mentioning that already existing rlist and stailq list implementations are not suitable: rlist is cycled list, as a result it is impossible to start iteration over the list from random list entry and finish it at the logical end of the list; stailq is single-linked list leaving no possibility to remove elements from the middle of the list. As a part of C interface, box_error_add() has been introduced. In contrast to box_error_set() it does not replace last raised error, but instead it adds error to the list of diagnostic errors having already been set. If error is to be deleted (its reference counter hits 0 value) it is unlinked from the list it belongs to and destroyed. Meanwhile, error destruction leads to decrement of reference counter of its previous error and so on. To organize errors into lists in Lua, table representing error object in Lua now has .prev field (corresponding to 'previous' error) and method :set_prev(e). The latter accepts error object (i.e. created via box.error.new() or box.error.last()) and nil value. Both field .prev and :set_prev() method are implemented as ffi functions. Also note that cycles are not allowed while organizing errors into lists: e1 -> e2 -> e3; e3:set_prev(e1) -- would lead to error. Part of #1148
Since we've introduced stacked diagnostic in previous commit, let's use it in the code implementing functional indexes. Part of #1148
Refactor iproto_reply_error and iproto_write_error with a new mpstream-based helper mpstream_iproto_encode_error that encodes error object for iproto protocol on a given stream object. Previously each routine implemented an own error encoding, but with the increasing complexity of encode operation with following patches we need a uniform way to do it. The iproto_write_error routine starts using region location to use region-based mpstream. It is not a problem itself, because errors reporting is not really performance-critical path. Needed for #1148
This patch introduces support of stacked errors in IProto protocol and in net.box module. Closes #1148 @TarantoolBot document Title: Stacked error diagnostic area Starting from now errors can be organized into lists. To achieve this Lua table representing error object is extended with .prev field and e:set_prev(err) method. .prev field returns previous error if any exist. e:set_prev(err) method expects err to be error object or nil and sets err as previous error of e. For instance: ``` e1 = box.error.new({code = 111, reason = "cause"}) e2 = box.error.new({code = 111, reason = "cause of cause"}) e1:set_prev(e2) assert(e1.prev == e2) -- true ``` Cycles are not allowed for error lists: ``` e2:set_prev(e1) - error: 'builtin/error.lua: Cycles are not allowed' ``` Nil is valid input to :set_prev() method: ``` e1:set_prev(nil) assert(e1.prev == nil) -- true ``` Note that error can be 'previous' only to the one error at once: ``` e1:set_prev(e2) e3:set_prev(e2) assert(e1.prev == nil) -- true assert(e3.prev == e2) -- true ``` Setting previous error does not erase its own previous members: ``` -- e1 -> e2 -> e3 -> e4 e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e2:set_prev(e5) -- Now there are two lists: e1->e2->e5 and e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == e5) -- true assert(e3.prev == e4) -- true ``` Alternatively: ``` e1:set_prev(e2) e2:set_prev(e3) e3:set_prev(e4) e5:set_prev(e3) -- Now there are two lists: e1->e2 and e5->e3->e4 assert(e1.prev == e2) -- true assert(e2.prev == nil) -- true assert(e5.prev == e3) -- true assert(e3.prev == e4) -- true `` Stacked diagnostics is also supported by IProto protocol. Now responses containing errors always (even if there's only one error to be returned) include new IProto key: IPROTO_ERROR_STACK (0x51). So, body corresponding to error response now looks like: ``` MAP{IPROTO_ERROR : string, IPROTO_ERROR_STACK : ARRAY[MAP{ERROR_CODE : uint, ERROR_MESSAGE : string}, MAP{...}, ...]} ``` where IPROTO_ERROR is 0x31 key, IPROTO_ERROR_STACK is 0x52, ERROR_CODE is 0x01 and ERROR_MESSAGE is 0x02. Instances of older versions (without support of stacked errors in protocol) simply ignore unknown keys and still rely only on IPROTO_ERROR key.
In terms of implementation, now struct error objects can be organized into double-linked lists. To achieve this pointers to the next and previous elements (cause and effect correspondingly) have been added to struct error. It is worth mentioning that already existing rlist and stailq list implementations are not suitable: rlist is cycled list, as a result it is impossible to start iteration over the list from random list entry and finish it at the logical end of the list; stailq is single-linked list leaving no possibility to remove elements from the middle of the list. As a part of C interface, box_error_add() has been introduced. In contrast to box_error_set() it does not replace last raised error, but instead it adds error to the list of diagnostic errors having already been set. If error is to be deleted (its reference counter hits 0 value) it is unlinked from the list it belongs to and destroyed. Meanwhile, error destruction leads to decrement of reference counter of its previous error and so on. To organize errors into lists in Lua, table representing error object in Lua now has .prev field (corresponding to 'previous' error) and method :set_prev(e). The latter accepts error object (i.e. created via box.error.new() or box.error.last()) and nil value. Both field .prev and :set_prev() method are implemented as ffi functions. Also note that cycles are not allowed while organizing errors into lists: e1 -> e2 -> e3; e3:set_prev(e1) -- would lead to error. Part of #1148
Since we've introduced stacked diagnostic in previous commit, let's use it in the code implementing functional indexes. Part of #1148
Refactor iproto_reply_error and iproto_write_error with a new mpstream-based helper mpstream_iproto_encode_error that encodes error object for iproto protocol on a given stream object. Previously each routine implemented an own error encoding, but with the increasing complexity of encode operation with following patches we need a uniform way to do it. The iproto_write_error routine starts using region location to use region-based mpstream. It is not a problem itself, because errors reporting is not really performance-critical path. Needed for #1148
Add support for multiple errors in
struct diag
. Accumulate all occurred errors during processing a request instruct diag
.The text was updated successfully, but these errors were encountered: