Derby
  1. Derby
  2. DERBY-4520

Refactor and extend data type cloning facilities

    Details

    • Type: Improvement Improvement
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 10.6.1.0
    • Fix Version/s: 10.6.1.0
    • Component/s: Store
    • Labels:
      None

      Description

      With the increased use of streams to represent data values, the cloning facilities needs to be improved.

      Unless I get pushback, I will proceed by producing patches to reach the following goals:

      • move the functionality provided by CloneableObject into DataValueDescriptor
        (all classes implementing CloneableObject also implements DataValueDescriptor)
      • introduce the cloning methods cloneValue, cloneState and cloneHolder (all in DataValueDescriptor, see description below)
        Note that they all return a usable DVD. I'm all ears for better names for the clone methods (another suggestion mentioned is cloneDeep, cloneHalfDeep, and cloneShallow).

      NOTE: See comment below, the method names changed during the course of development.

      cloneValue <deep> (new method, functionality was present through combined calls to the DVD public interface)

      • a DVD obtained through cloneValue is independent of other DVDs and the state of the Derby store
      • the data value will be materialized

      cloneState <halfDeep> (~= DataValueDescriptor.getClone)

      • a DVD obtained through cloneState is independent of other DVDs, but may depend on the state of the Derby store (due to references to store streams)
      • the data value will be materialized if the value is represented by a non-cloneable stream or if Derby believes materializing the value is more appropriate than keeping the stream representation

      cloneHolder <shallow> (~= CloneableObject.cloneObject)

      • a DVD obtained through cloneHolder is dependent on the original DVD and its clones made through cloneHolder. If one of the DVDs changes its state, all of them will be affected. Will also be dependent on the state of the Derby store if there are references to store streams.
      • the data value will never be materialized due to cloneHolder being invoked

      For many of the data types, cloneState and cloneHolder will forward to cloneValue.

      cloneState will be used the most. cloneValue is currently only required in the sorter. cloneHolder is required (for performance reasons and maybe to avoid OOME) when DVDs pass through temporary holders (BackingStoreHashtable, TemporaryRowHolderImpl). I have not gone through all the usages of cloneState to see if any of them can be, or has to be, replaced with another clone-call.

      The ability to clone store streams will be added by Mike's patch attached to DERBY-3650.

      • New method names:
        It turned out that using only two methods was sufficient:
        cloneHolder <shallow> (as above)

      cloneValue(boolean forceMaterialization) <halfDeep or deep>
      Basically, cloneValue(false) equals cloneState() above, and cloneValue(true) equals cloneValue() above.

      1. derby-4520-7b-lob_clonevalue_methods.diff
        8 kB
        Kristian Waagan
      2. derby-4520-7a-lob_clonevalue_methods.diff
        8 kB
        Kristian Waagan
      3. derby-4520-6a-unused_imports.diff
        7 kB
        Kristian Waagan
      4. derby-4520-5a-getClone_renamed_cloneValue.stat
        3 kB
        Kristian Waagan
      5. derby-4520-5a-getClone_renamed_cloneValue.diff
        44 kB
        Kristian Waagan
      6. derby-4520-4a-cloneObject_renamed_cloneHolder.diff
        7 kB
        Kristian Waagan
      7. derby-4520-3b-CloneableStream_and_delayed_fill.diff
        9 kB
        Kristian Waagan
      8. derby-4520-3b-CloneableStream_and_delayed_fill.stat
        0.4 kB
        Kristian Waagan
      9. derby-4520-3a-CloneableStream_and_delayed_fill.stat
        0.4 kB
        Kristian Waagan
      10. derby-4520-3a-CloneableStream_and_delayed_fill.diff
        8 kB
        Kristian Waagan
      11. derby-4520-2a-remove_CloneableObject_iface.stat
        0.7 kB
        Kristian Waagan
      12. derby-4520-2a-remove_CloneableObject_iface.diff
        13 kB
        Kristian Waagan
      13. derby-4520-1a-RowLocation_cloning.diff
        6 kB
        Kristian Waagan
      14. derby-4520-1a-RowLocation_cloning.diff
        4 kB
        Kristian Waagan

        Issue Links

          Activity

          Hide
          Kristian Waagan added a comment -

          Patch 1a removes the use of CloneableObject.cloneObject for RowLocation because it isn't needed. DataValueDescriptor.getClone is used instead.
          There is an assert in MergeSort (line 462), checking if the objects implement CloneableObject. HeapRowLocation extends DataType, which implements CloneableObject. The assert will be removed in a follow-up patch, together with CloneableObject itself.

          I rewrote the assert code in BaseActivation to avoid creating two clones of the row location in sane builds.

          I plan to commit this patch very soon, after I have once more run the regression tests.
          After these changes, there are just a few usages of CloneableObject.cloneObject left. The method will be moved into DataValueDescriptor.

          Show
          Kristian Waagan added a comment - Patch 1a removes the use of CloneableObject.cloneObject for RowLocation because it isn't needed. DataValueDescriptor.getClone is used instead. There is an assert in MergeSort (line 462), checking if the objects implement CloneableObject. HeapRowLocation extends DataType, which implements CloneableObject. The assert will be removed in a follow-up patch, together with CloneableObject itself. I rewrote the assert code in BaseActivation to avoid creating two clones of the row location in sane builds. I plan to commit this patch very soon, after I have once more run the regression tests. After these changes, there are just a few usages of CloneableObject.cloneObject left. The method will be moved into DataValueDescriptor.
          Hide
          Knut Anders Hatlen added a comment -

          Patch 1a looks like a good improvement to me.

          Show
          Knut Anders Hatlen added a comment - Patch 1a looks like a good improvement to me.
          Hide
          Kristian Waagan added a comment -

          Thanks for having a look at the patch, Knut Anders.

          Committed patch 1a to trunk with revision 901642.

          Show
          Kristian Waagan added a comment - Thanks for having a look at the patch, Knut Anders. Committed patch 1a to trunk with revision 901642.
          Hide
          Kristian Waagan added a comment -

          Patch 2a removes the interface CloneableObject.
          I moved the method cloneObject into DataValueDescriptor, and plan to rename it (or maybe merge it with another clone-method). Note that the method now returns a DataValueDescriptor, not an Object.
          Updated some comments and removed some unused imports.
          Regression tests passed.

          Patch ready for review.

          Show
          Kristian Waagan added a comment - Patch 2a removes the interface CloneableObject. I moved the method cloneObject into DataValueDescriptor, and plan to rename it (or maybe merge it with another clone-method). Note that the method now returns a DataValueDescriptor, not an Object. Updated some comments and removed some unused imports. Regression tests passed. Patch ready for review.
          Hide
          Kristian Waagan added a comment -

          Committed patch 2a to trunk with revision 902050.

          I'll move on to provide the patch for cloning a store stream (extracting the code from the patch attached to DERBY-3650).
          Note that cloning a store stream doesn't materialize the data value. Another stream object will be instantiated which will read data from the same data pages as the original stream.

          Show
          Kristian Waagan added a comment - Committed patch 2a to trunk with revision 902050. I'll move on to provide the patch for cloning a store stream (extracting the code from the patch attached to DERBY-3650 ). Note that cloning a store stream doesn't materialize the data value. Another stream object will be instantiated which will read data from the same data pages as the original stream.
          Hide
          Knut Anders Hatlen added a comment -

          Patch 2a looks good to me. I found one of the comments a little unclear, though:

          + // History: We used to not materialize streams when getting a clone
          + // here (i.e. used cloneObject, not getClone). We still do.
          + // DERBY-802

          Does "We still do" mean that we still materialize the stream, or that we still do not materialize the stream?

          Show
          Knut Anders Hatlen added a comment - Patch 2a looks good to me. I found one of the comments a little unclear, though: + // History: We used to not materialize streams when getting a clone + // here (i.e. used cloneObject, not getClone). We still do. + // DERBY-802 Does "We still do" mean that we still materialize the stream, or that we still do not materialize the stream?
          Hide
          Kristian Waagan added a comment -

          Attached patch 3a, which does the following:

          • adds the required code for cloning store streams
          • modified OverflowInputStream to fill the byte array on the first read instead of at construction/initialization time, which also allowed me to remove some throws-clauses

          The delayed buffer filling should reduce the penalty in cases where we clone and the user never actually access the value. I had to fix the broken/inaccurate error reporting in OverflowInputStream.fillByteHolder to make the regression tests pass. The cause for the failures was that the error got reported in a different way because the real exception got lost.
          Most of the code is from Mike's patch, but I did make some changes. Also note that I haven't included any of the real fixes from derby-3650-preliminary_2_diff.txt (see DERBY-3650).

          Patch ready for review.

          The next step is to introduce and/or modify the various clone methods we need in DataValueDescriptor, wiring in the stream cloning where appropriate.

          Show
          Kristian Waagan added a comment - Attached patch 3a, which does the following: adds the required code for cloning store streams modified OverflowInputStream to fill the byte array on the first read instead of at construction/initialization time, which also allowed me to remove some throws-clauses The delayed buffer filling should reduce the penalty in cases where we clone and the user never actually access the value. I had to fix the broken/inaccurate error reporting in OverflowInputStream.fillByteHolder to make the regression tests pass. The cause for the failures was that the error got reported in a different way because the real exception got lost. Most of the code is from Mike's patch, but I did make some changes. Also note that I haven't included any of the real fixes from derby-3650-preliminary_2_diff.txt (see DERBY-3650 ). Patch ready for review. The next step is to introduce and/or modify the various clone methods we need in DataValueDescriptor, wiring in the stream cloning where appropriate.
          Hide
          Kristian Waagan added a comment -

          Knut> [confusing comment] Does "We still do" mean that we still materialize the stream, or that we still do not materialize the stream?

          It means we still don't materialize. However, I might change the way the stream is cloned (using the terms in the issue description, either cloneState or cloneHolder).
          I will clarify the comment in a later patch.

          Show
          Kristian Waagan added a comment - Knut> [confusing comment] Does "We still do" mean that we still materialize the stream, or that we still do not materialize the stream? It means we still don't materialize. However, I might change the way the stream is cloned (using the terms in the issue description, either cloneState or cloneHolder). I will clarify the comment in a later patch.
          Hide
          Knut Anders Hatlen added a comment -

          Patch 3a looks fine, as far as I can tell. A couple of questions:

          • Would it be better to place CloneableStream in iapi.services.io instead of iapi.types?
          • Do you think it would also make sense to remove the call to fillByteHolder() from OverflowInputStream.resetStream(), based on the same reasoning that led to its removal from OverflowInputStream's constructor?
          Show
          Knut Anders Hatlen added a comment - Patch 3a looks fine, as far as I can tell. A couple of questions: Would it be better to place CloneableStream in iapi.services.io instead of iapi.types? Do you think it would also make sense to remove the call to fillByteHolder() from OverflowInputStream.resetStream(), based on the same reasoning that led to its removal from OverflowInputStream's constructor?
          Hide
          Kristian Waagan added a comment -

          Thanks for the suggestions, Knut Anders.

          Moving the interface definitely makes sense.
          I also removed the call to fillByteBuffer in OverflowInputStream.resetStream and ran the regression tests. No tests failed.
          I had a look at the code using the resetStream method, and I think the only thing we change is that any exceptions raised by fillByteBuffer will occur in an InputStream-call instead of in resetStream. In most cases the resetStream is called first, followed by a call to either read or skip.
          I don't think this last change will have any effect on performance, but it is better to keep the behavior consistent across the different methods.

          I incorporated both changes into patch 3b, and committed it to trunk with revision 902742.

          Show
          Kristian Waagan added a comment - Thanks for the suggestions, Knut Anders. Moving the interface definitely makes sense. I also removed the call to fillByteBuffer in OverflowInputStream.resetStream and ran the regression tests. No tests failed. I had a look at the code using the resetStream method, and I think the only thing we change is that any exceptions raised by fillByteBuffer will occur in an InputStream-call instead of in resetStream. In most cases the resetStream is called first, followed by a call to either read or skip. I don't think this last change will have any effect on performance, but it is better to keep the behavior consistent across the different methods. I incorporated both changes into patch 3b, and committed it to trunk with revision 902742.
          Hide
          Knut Anders Hatlen added a comment -

          Thanks for making these changes, Kristian. One nit that I missed the last time:

          + public InputStream cloneStream() {
          + if (SanityManager.DEBUG)
          + SanityManager.ASSERT(in instanceof CloneableStream);

          + InputStream new_input_stream = ((CloneableStream) in).cloneStream();

          Here, the ASSERT is actually making debugging harder, so I think it's better to remove it. The only thing we will see in debug builds is "assert failed", whereas non-debug builds will show the more helpful message "ClassCastException: SomeStreamClass cannot be cast to CloneableStream".

          Show
          Knut Anders Hatlen added a comment - Thanks for making these changes, Kristian. One nit that I missed the last time: + public InputStream cloneStream() { + if (SanityManager.DEBUG) + SanityManager.ASSERT(in instanceof CloneableStream); + InputStream new_input_stream = ((CloneableStream) in).cloneStream(); Here, the ASSERT is actually making debugging harder, so I think it's better to remove it. The only thing we will see in debug builds is "assert failed", whereas non-debug builds will show the more helpful message "ClassCastException: SomeStreamClass cannot be cast to CloneableStream".
          Hide
          Kristian Waagan added a comment -

          I wonder if we should just remove the same asserts for init-, close- and resetStream in FormatIdInputStream as well?

          Show
          Kristian Waagan added a comment - I wonder if we should just remove the same asserts for init-, close- and resetStream in FormatIdInputStream as well?
          Hide
          Knut Anders Hatlen added a comment -

          Yes, +1 to removing them from FormatIdInputStream too.

          Show
          Knut Anders Hatlen added a comment - Yes, +1 to removing them from FormatIdInputStream too.
          Hide
          Kristian Waagan added a comment -

          Removed unhelpful asserts, and added @see tags to copy JavaDoc from the interfaces, with revision 902798.

          Show
          Kristian Waagan added a comment - Removed unhelpful asserts, and added @see tags to copy JavaDoc from the interfaces, with revision 902798.
          Hide
          Kristian Waagan added a comment -

          FYI, I've seen the following stream classes passed in to FormatIdInputStream (this list may not be exhaustive):
          java.io.ByteArrayInputStream
          org.apache.derby.iapi.services.io.ArrayInputStream
          org.apache.derby.iapi.services.io.LimitInputStream
          org.apache.derby.iapi.types.RawToBinaryFormatStream
          org.apache.derby.iapi.types.ReaderToUTF8Stream
          org.apache.derby.impl.store.raw.data.OverflowInputStream

          Show
          Kristian Waagan added a comment - FYI, I've seen the following stream classes passed in to FormatIdInputStream (this list may not be exhaustive): java.io.ByteArrayInputStream org.apache.derby.iapi.services.io.ArrayInputStream org.apache.derby.iapi.services.io.LimitInputStream org.apache.derby.iapi.types.RawToBinaryFormatStream org.apache.derby.iapi.types.ReaderToUTF8Stream org.apache.derby.impl.store.raw.data.OverflowInputStream
          Hide
          Kristian Waagan added a comment -

          Attached patch 4a, a small patch renaming cloneObject to cloneHolder and modifying a few comments.

          Patch ready for review.
          I hope to commit this tomorrow. Running tests again now due to a last minute change.

          Show
          Kristian Waagan added a comment - Attached patch 4a, a small patch renaming cloneObject to cloneHolder and modifying a few comments. Patch ready for review. I hope to commit this tomorrow. Running tests again now due to a last minute change.
          Hide
          Kristian Waagan added a comment -

          Committed patch 4a to trunk with revision 903150.

          Show
          Kristian Waagan added a comment - Committed patch 4a to trunk with revision 903150.
          Hide
          Dag H. Wanvik added a comment -

          Patch 4a looks good to me.

          The issue description puzzled me in the explanatiion of cloneHolder
          <shallow>":

          :

          • the data value will never be materialized due to cloneHolder being invoked

          For many of the data types, cloneState and cloneHolder will forward
          to cloneValue.

          But for cloneValue you say: "- the data value will be materialized",
          so presumablu the sentence above should read:

          • for streams, the data value will never be materialized due to
            cloneHolder being invoked

          or some such?

          Show
          Dag H. Wanvik added a comment - Patch 4a looks good to me. The issue description puzzled me in the explanatiion of cloneHolder <shallow>": : the data value will never be materialized due to cloneHolder being invoked For many of the data types, cloneState and cloneHolder will forward to cloneValue. But for cloneValue you say: "- the data value will be materialized", so presumablu the sentence above should read: for streams, the data value will never be materialized due to cloneHolder being invoked or some such?
          Hide
          Kristian Waagan added a comment -

          Hi Dag,

          Yes, you are right - this is all about streams I'll edit the description in a little while (just want to wait for another potential change).
          Regarding the non-stream values, I think it is still correct to say they will be materialized by cloneValue. I haven't verified this for all data types, but for instance SQLDecimal will transform the value from a byte array (all in memory) to a BigDecimal-object when cloning. If the value is already a BigDecimal, it is shared between the original and the clone (which is fine, BigDecimal is immutable).

          When it comes to the methods from the description, I'm considering merging cloneState and cloneValue into cloneValue(boolean forceMaterialization): false will equal cloneState, true will equal the cloneValue-method from the issue description.
          Running the regression tests with getClone changed to cloneValue(true) was successful.
          Changing all these to cloneValue(false) resulted in a total of 21 (four distinct error messages) test failures in the following tests:
          org.apache.derbyTesting.functionTests.tests.jdbc4.LobSortTest
          org.apache.derbyTesting.functionTests.tests.lang.TriggerTest
          org.apache.derbyTesting.functionTests.tests.store.StreamingColumnTest
          org.apache.derbyTesting.functionTests.tests.tools.ImportExportLobTest

          I don't know yet if the streams causing these errors are coming from the store or from triggers, procedures or the user - I'll look into this.

          Show
          Kristian Waagan added a comment - Hi Dag, Yes, you are right - this is all about streams I'll edit the description in a little while (just want to wait for another potential change). Regarding the non-stream values, I think it is still correct to say they will be materialized by cloneValue. I haven't verified this for all data types, but for instance SQLDecimal will transform the value from a byte array (all in memory) to a BigDecimal-object when cloning. If the value is already a BigDecimal, it is shared between the original and the clone (which is fine, BigDecimal is immutable). When it comes to the methods from the description, I'm considering merging cloneState and cloneValue into cloneValue(boolean forceMaterialization): false will equal cloneState, true will equal the cloneValue-method from the issue description. Running the regression tests with getClone changed to cloneValue(true) was successful. Changing all these to cloneValue(false) resulted in a total of 21 (four distinct error messages) test failures in the following tests: org.apache.derbyTesting.functionTests.tests.jdbc4.LobSortTest org.apache.derbyTesting.functionTests.tests.lang.TriggerTest org.apache.derbyTesting.functionTests.tests.store.StreamingColumnTest org.apache.derbyTesting.functionTests.tests.tools.ImportExportLobTest I don't know yet if the streams causing these errors are coming from the store or from triggers, procedures or the user - I'll look into this.
          Hide
          Kristian Waagan added a comment -

          Attached patch 5a, which main purposes are to rename getClone to cloneValue and to add the boolean argument 'forceMaterialization'.

          Regarding the methods mentioned in the issue description, 'cloneValue(true)' equals 'cloneValue' and 'cloneValue(false)' equals 'cloneState'.
          The default value of 'forceMaterialization' has been set to false. I'm aware of one location where it has to be switched to true (BasicSortObserver). Note that I have not yet changed the methods to actually use the new variable, and further that many types won't care about its value because they are in a way always materialized (i.e. the value is a primitive, or represented by an immutable object).

          Besides from formatting changes and the renaming, I made a few other changes as well:

          • GenericParameter
            Removed a bunch of unused imports (iapi.types.* was imported trice).
          • ValueRow
            Removed special handling of RowLocation in 'getNewNullRow'.
          • SQLSmallInt, SQLTinyInt
            Made constructor used for cloning only private.
          • XML
            Added argument in contructor used for cloning to tell whether the underlying source should be materialized or not.

          Regression tests passed.
          Patch ready for review.

          NOTE: Some of our clone methods are unsafe in the sense that it is possible to change a value which is shared between two or more holders (DVDs). One such example is values represented by a byte array.

          Show
          Kristian Waagan added a comment - Attached patch 5a, which main purposes are to rename getClone to cloneValue and to add the boolean argument 'forceMaterialization'. Regarding the methods mentioned in the issue description, 'cloneValue(true)' equals 'cloneValue' and 'cloneValue(false)' equals 'cloneState'. The default value of 'forceMaterialization' has been set to false. I'm aware of one location where it has to be switched to true (BasicSortObserver). Note that I have not yet changed the methods to actually use the new variable, and further that many types won't care about its value because they are in a way always materialized (i.e. the value is a primitive, or represented by an immutable object). Besides from formatting changes and the renaming, I made a few other changes as well: GenericParameter Removed a bunch of unused imports (iapi.types.* was imported trice). ValueRow Removed special handling of RowLocation in 'getNewNullRow'. SQLSmallInt, SQLTinyInt Made constructor used for cloning only private. XML Added argument in contructor used for cloning to tell whether the underlying source should be materialized or not. Regression tests passed. Patch ready for review. NOTE: Some of our clone methods are unsafe in the sense that it is possible to change a value which is shared between two or more holders (DVDs). One such example is values represented by a byte array.
          Hide
          Kristian Waagan added a comment -

          Committed patch 5a to trunk with revision 906595.
          I'll incorporate any feedback in a later patch.

          Show
          Kristian Waagan added a comment - Committed patch 5a to trunk with revision 906595. I'll incorporate any feedback in a later patch.
          Hide
          Kristian Waagan added a comment -

          I factored out the removal of unused imports in the data type sub-trees starting with SQLBinary and SQLChar (which are the most relevant ones regarding the cloning).
          Attached as patch 6a and committed to trunk with revision 906609.

          Show
          Kristian Waagan added a comment - I factored out the removal of unused imports in the data type sub-trees starting with SQLBinary and SQLChar (which are the most relevant ones regarding the cloning). Attached as patch 6a and committed to trunk with revision 906609.
          Hide
          Kristian Waagan added a comment -

          Patch 7a changes the cloneValue methods for SQLBlob and SQLClob.
          The general idea for the character and binary values is that all streams are materialized when cloning the value, expect for BLOB and CLOB.
          Note that BasicSortObserver is now forcing materialization of streams. This is how it used to be (before patch 5a), but I think this may cause problems for a few types of queries - for instance 'select * from mytable order by length(myClob)'. This will have to be addressed under a different Jira.

          Also note that there are a few TODOs in the clone methods. I'd appreciate feedback on any of these if you have something on your mind.

          Regression tests passed (re-running as well to be sure).
          Patch ready for review.

          Show
          Kristian Waagan added a comment - Patch 7a changes the cloneValue methods for SQLBlob and SQLClob. The general idea for the character and binary values is that all streams are materialized when cloning the value, expect for BLOB and CLOB. Note that BasicSortObserver is now forcing materialization of streams. This is how it used to be (before patch 5a), but I think this may cause problems for a few types of queries - for instance 'select * from mytable order by length(myClob)'. This will have to be addressed under a different Jira. Also note that there are a few TODOs in the clone methods. I'd appreciate feedback on any of these if you have something on your mind. Regression tests passed (re-running as well to be sure). Patch ready for review.
          Hide
          Kristian Waagan added a comment -

          I need to provide a new revision of patch 7, as I forgot to modify CollatorSQLClob. As it stands now, these CLOB values will always be materialized. I guess we don't have any tests for large collator CLOBs running in VMs with a small heap...

          Show
          Kristian Waagan added a comment - I need to provide a new revision of patch 7, as I forgot to modify CollatorSQLClob. As it stands now, these CLOB values will always be materialized. I guess we don't have any tests for large collator CLOBs running in VMs with a small heap...
          Hide
          Kristian Waagan added a comment -

          Attached patch 7b, which corrects two typos (BLOB -> CLOB) and adds a shortcut for cloning NULLs.
          The shortcut feels better, because using the fall-back case (calling clone.setValue(getBytes()) results in the two DVDs referencing the same byte array for BLOBs.
          I have decided to create a separate Jira for CollatorSQLClob, see DERBY-4542.

          Committed to trunk with revision 907732.

          Show
          Kristian Waagan added a comment - Attached patch 7b, which corrects two typos (BLOB -> CLOB) and adds a shortcut for cloning NULLs. The shortcut feels better, because using the fall-back case (calling clone.setValue(getBytes()) results in the two DVDs referencing the same byte array for BLOBs. I have decided to create a separate Jira for CollatorSQLClob, see DERBY-4542 . Committed to trunk with revision 907732.
          Hide
          Kristian Waagan added a comment -

          Changed issue description to mention the resulting two methods (instead of three).

          Also, I'm not sure if more work needs to be done under this issue, but I'll keep it open for while.

          Show
          Kristian Waagan added a comment - Changed issue description to mention the resulting two methods (instead of three). Also, I'm not sure if more work needs to be done under this issue, but I'll keep it open for while.
          Hide
          Kristian Waagan added a comment -

          Closing issue, related / additional work will be done in separate Jiras.

          Show
          Kristian Waagan added a comment - Closing issue, related / additional work will be done in separate Jiras.

            People

            • Assignee:
              Kristian Waagan
              Reporter:
              Kristian Waagan
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development