Project

General

Profile

Bug #9318

Make record nursery visible on removeRecords

Added by Alexandru Lungu over 1 year ago. Updated about 1 year ago.

Status:
Review
Priority:
Normal
Target version:
-
Start date:
Due date:
% Done:

90%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:
production:
No
env_name:
topics:

History

#1 Updated by Alexandru Lungu over 1 year ago

  • Assignee set to Stefanel Pezamosca

containsRecordBufferDmo was a quick fix to have the 6667i branch fast tracked into trunk. It was a very aggressive change to guarantee functionality at the expense of performance. In reality, the best approach was to actually make the nursery visible inside removeRecords so that the records would have been flushed to the primary database before attempting to remove the records using a DELETE. We didn't take such decision then because it was riskier and required more testing. Also, there is a functional issue: removeRecords is reached from other code than EMPTY TEMP-TABLE (e.g. for each tt: delete tt. end.), in which case the index should be honored (i.e. only the records visible on a specific index should be deleted / made visible).

This was discussed before in #9280 and the resolution is in #8959-19. However, please wait the merge of #9275 first as it has a broad refactoring of the RecordNursery anyway.

#3 Updated by Alexandru Lungu over 1 year ago

  • Assignee changed from Stefanel Pezamosca to Dănuț Filimon

#4 Updated by Alexandru Lungu over 1 year ago

9275a was merged recently to trunk. The same changes reached 7156c at some point. Danut, please take over this as part of your work on #9244.

#5 Updated by Dănuț Filimon over 1 year ago

  • Status changed from New to WIP

#6 Updated by Dănuț Filimon over 1 year ago

From #8959-19:

The only limitation is that deleteIndex should come from the converted code and requires conversion changes.

I am wondering how should this look in the converted code? The note also mentions using null as the value of deleteIndex as a starting point, but I am not sure how I would add this index after I start working on the serious changes.

As for the RecordNursery, it is easily accessible in removeRecords() so I don't expect any problems working on this part.

#7 Updated by Dănuț Filimon over 1 year ago

Created 9318a from trunk/15586 and committed an initial implementation on 9318a/15587. Allows to make a distinction between EMPTY-TEMP-TABLE and other bulk delete operations and honors the RecordNursery in removeRecords (but the deleteIndex is not set). Still thinking about #9318-6.

#8 Updated by Alexandru Lungu over 1 year ago

I am wondering how should this look in the converted code?

Well, the converted code is now tt.deleteAll() and it should be something like tt.deleteAll(...) (either the index name or the ORDER BY clause from which we can detect the index at run-time).

The note also mentions using null as the value of deleteIndex as a starting point, but I am not sure how I would add this index after I start working on the serious changes.

I meant making the existing deleteAll() call the deleteAll(deleteIndex) with null and start designing the implementation of deleteAll(deleteIndex). Yeah, the run-time functionality will be hit only for the case when deleteIndex is null, but will allow you to design the run-time without making the conversion changes first. You can make the conversion changes last and simply generate deleteAll(deleteIndex), which will be already implemented.

#9 Updated by Dănuț Filimon over 1 year ago

Alexandru Lungu wrote:

I meant making the existing deleteAll() call the deleteAll(deleteIndex) with null and start designing the implementation of deleteAll(deleteIndex). Yeah, the run-time functionality will be hit only for the case when deleteIndex is null, but will allow you to design the run-time without making the conversion changes first. You can make the conversion changes last and simply generate deleteAll(deleteIndex), which will be already implemented.

Thank you, I will have to rethink this a bit because I introduced a deleteAll(boolean). EMPTY TEMP-TABLE does convert to a deleteAll(), but it's also called internally in FWD so I had to make this distinction first.

#10 Updated by Dănuț Filimon over 1 year ago

Currently:
  • EMPTY TEMP-TABLE should honor all indices;
  • FOR EACH tt1. DELETE tt1. END. should honor the primary index;
  • FOR EACH tt1 WHERE <condition>. DELETE tt1. END. that should honor a delete index.

The for each with a where clause is converted in the following way:

Buffer.delete(tt1, "<where_clause>");
which also ends up in removeRecords() so we will have a way to get our hands on the delete index.

#11 Updated by Dănuț Filimon over 1 year ago

My point for #9318-10 is that we do not need a deleteAll(deleteIndex), we only need to make the distinction between deleteAll() and deleteForAll() because for each with a where does not make use of these two methods.

#12 Updated by Dănuț Filimon over 1 year ago

  • Status changed from WIP to Review
  • % Done changed from 0 to 30
  • reviewer Alexandru Lungu added

Buffer.delete() is used for temporary tables because there is no order by clause generated at conversion, but a recid asc order by clause is added for persistent tables and this transforms the query into a PreselectQuery.

How it looks in the converted code:

   @LegacySignature(type = Type.METHOD, name = "test5")
   @Test(order = 6)
   public void test5()
   {
      internalProcedure(Test9318EmptyTempTableIndex.class, this, "test5", new Block((Body) () -> 
      {
         Buffer.delete(tt9318, "upper(f2) = 'YT'", "index11");
      }));
   }

My changes include:
  • RecordMeta - method to retrieve all index uids.
  • TemporaryBuffer - honor delete index/primary index/all indices in removeRecords(), a delete() overload to receive the delete index from a Buffer.delete() call. A deleteAll() overload to make it possible to distinguish between EMPTY TEMP-TABLE and FOR EACH .. DELETE .. END (sets implicitBulkDelete).
  • RecordBuffer - added overloads mentioned in TemporaryBuffer
  • Buffer - added overload to pass the delete index
  • AbstractTempTable - part of distinguishing between EMPTY TEMP-TABLE and FOR EACH .. DELETE .. END
  • database_access - adds the index to a Buffer.delete() call, this only happens for temp-tables
  • index_selection - adds the implicit_index annotation to a record_phrase (this is then used in database_access)

Alexandru, can you do a partial review of this implementation? I want to have your opinion on this and #9318-10 - #9318-11.

#13 Updated by Dănuț Filimon over 1 year ago

Latest implementation was committed to 9318a/15588

#14 Updated by Dănuț Filimon over 1 year ago

Rebased 9318a to latest trunk/15616, the branch is now at revision 15618.

#15 Updated by Alexandru Lungu over 1 year ago

  • Status changed from Review to WIP
  • % Done changed from 30 to 80
I am OK with the "backbone" of 9318a.
  • However, I am not enjoying the implicitBulkDelete and deleteIndex class members. The setter and getter of these flags are not that far away, so I would prefer to propagate them by arguments, instead of saving them at class-level. The more class members, the more a risk to unsync them.

#16 Updated by Dănuț Filimon over 1 year ago

  • % Done changed from 80 to 100
  • Status changed from WIP to Review

Addressed the review from #9318-15 in 9318a/15589. I removed implicitBulkDelete and deleteIndex members and created new overloads to pass them as arguments.

#17 Updated by Alexandru Lungu over 1 year ago

9318a is at rev. 15618 (which doesn't include your latest changes).

#18 Updated by Dănuț Filimon over 1 year ago

Alexandru Lungu wrote:

9318a is at rev. 15618 (which doesn't include your latest changes).

Sorry about that, the changes are available now in 9318a/15619.

#19 Updated by Alexandru Lungu over 1 year ago

  • Status changed from Review to WIP
  • % Done changed from 100 to 80
Review of 9318a:
  • Please avoid overloading removeRecords and enforce all callers to decide what kind of delete they want. There is no clear default. You chose implicit = false and deleteIndex = null, but this means honoring the nursery on surrogate PK (recid). This is not necessary intuitive for a bulk delete. Maybe honor on all indexes will suite better. Or maybe honor on the original 4GL PK, that may be built upon a specific field. My point is that we can't assess any reasonable default here.
    • Do the same for deleteAllImpl and enforce callers to decide whether they are implicit or not. Do not overload!
  • Please remove forceLoopingDelete conditional from forceLoopingDelete. The whole goal of this task is to avoid forcing loop delete if the RN has records. It is better to simply honor the nursery on the right index.
    • Without this change, your modifications aren't taking effect. Please consider proper debugging to check how your changes take effect.
  • Delete on all indexes is not right. Unique indexes have positive idx and non-unique indexes have negative idx - see how RecordMeta.indexIdByName is constructed. You need to iterate from -rm.getNonuniqueIndexNames().length() to +rn.getUniqueIndexNames.length() or something like that. Please make tests and confirm by debugging.
    • Thus, I don't think getAllIndexIds is required.

#20 Updated by Dănuț Filimon over 1 year ago

Rebased 9318a to latest trunk/15676, the branch is now at revision 15679.

Currently working on addressing the #9318-19 review.

#21 Updated by Dănuț Filimon over 1 year ago

  • % Done changed from 80 to 90

Alexandru Lungu wrote:

Review of 9318a:
  • Please avoid overloading removeRecords and enforce all callers to decide what kind of delete they want. There is no clear default. You chose implicit = false and deleteIndex = null, but this means honoring the nursery on surrogate PK (recid). This is not necessary intuitive for a bulk delete. Maybe honor on all indexes will suite better. Or maybe honor on the original 4GL PK, that may be built upon a specific field. My point is that we can't assess any reasonable default here.

The implementation defaults to honoring the primary index, indeed there is no clear default so I used implicit = false and deleteIndex = null. The idea is that if we have a delete index, then we will expect the implicit value to be false so that it is honored. There are three scenarios as mentioned in #9318-10, I'll have to determine this through testing as I checked if the implicit and delete index are properly set for the mentioned scenarios, while leaving the other usages of removeRecords as they were and used overloads.

  • Delete on all indexes is not right. Unique indexes have positive idx and non-unique indexes have negative idx - see how RecordMeta.indexIdByName is constructed. You need to iterate from -rm.getNonuniqueIndexNames().length() to +rn.getUniqueIndexNames.length() or something like that. Please make tests and confirm by debugging.
    • Thus, I don't think getAllIndexIds is required.

indexIdByName contains both unique and nonunique indexes, the resulted array from getALlIndexIds can be sorted. The RecordNursery suggestion seems to be referring to what is used in RecordNursery.containsRecordBufferDmo(), which gets the index counts using

buffer.getDmoInfo().getIndexCount(false);
which relies on the RecordMeta (that is already available when we call removeRecords).

Thus using the following instead is better than relying on getAllIndexIds():

            int uniqueIndexesCount = rm.getUniqueIndexNames().length;
            int nonUniqueIndexesCount = rm.getNonuniqueIndexNames().length;
            for (int idxUid = -nonUniqueIndexesCount; idxUid <= uniqueIndexesCount; idxUid++)
            {
               rn.makeVisible(dmoId, multiplexID, idxUid);
            }
I reverted the changes from RecordMeta, but kept some missing javadoc for an undocumented method.

Another note for removeRecords, the Object[] args will have to be used as the final argument.

I committed 9318a/15680, still need to look into the default values. My unit tests pass, but I consider the best next step is to convert ETF and test the changes while I also extend the tests I already have.

#22 Updated by Dănuț Filimon over 1 year ago

ETF Conversion went well, ran two tests using the default implicit = false and deleteIndex = null, found no regressions. I also planned a round of testing using implicit = true and deleteIndex = null as the default.

#23 Updated by Dănuț Filimon over 1 year ago

I actually missed a regression in etf, so I am currently investigating it.

#24 Updated by Dănuț Filimon about 1 year ago

Rebased 9318a to latest trunk/15891, the branch is now at revision 15895.

#25 Updated by Dănuț Filimon about 1 year ago

I've tested ETF conversion & runtime with 9318a and found no issues with the current implementation, I will rebase to trunk/15894 to pick up the changes from 9945b and check if there are any other issues.

#26 Updated by Dănuț Filimon about 1 year ago

I rebased 9381a to trunk/15894 a while ago and the branch is now at revision 9318a/15898.

I also retested ETF at the time and didn't find any regressions, so the changes from 9945b did not affect the changes from 9318a (at least for ETF).

Alexandru, should I go ahead and create a test plan for 9318a? Is there something else I need to address in the implementation?

#27 Updated by Alexandru Lungu about 1 year ago

I rebased 9381a to trunk/15894 a while ago and the branch is now at revision 9318a/15898.

Please move this into Review and create a test plan. I will look into the changes soon.

#28 Updated by Dănuț Filimon about 1 year ago

  • Status changed from WIP to Review
9318a contains conversion changes for Buffer.delete() and runtime changes, the test plan is:
  • ETF (ddf) - already passed
  • ChUI regression tests
  • Customer 1 - smoke tests
  • Customer 2 - smoke tests and unit tests
  • Customer 3 - smoke tests, unit tests and fwdtests
  • Customer 4 - harness/report/navigation

I will be emailing everyone involved after the review.

Also available in: Atom PDF