Skip to content
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

HPCC-19337 Add streaming remote (dafilesrv) disk read support #10979

Merged
merged 1 commit into from Apr 11, 2018

Conversation

jakesmith
Copy link
Member

@jakesmith jakesmith commented Mar 20, 2018

Type of change:

  • This change is a bug fix (non-breaking change which fixes an issue).
  • This change is a new feature (non-breaking change which adds functionality).
  • This change improves the code (refactor or other change that does not change the functionality)
  • This change fixes warnings (the fix does not alter the functionality or the generated code)
  • This change is a breaking change (fix or feature that will cause existing behavior to change).
  • This change alters the query API (existing queries will have to be recompiled)

Checklist:

  • My code follows the code style of this project.
    • My code does not create any new warnings from compiler, build system, or lint.
  • The commit message is properly formatted and free of typos.
    • The commit message title makes sense in a changelog, by itself.
    • The commit is signed.
  • My change requires a change to the documentation.
    • I have updated the documentation accordingly, or...
    • I have created a JIRA ticket to update the documentation.
    • Any new interfaces or exported functions are appropriately commented.
  • I have read the CONTRIBUTORS document.
  • The change has been fully tested:
    • I have added tests to cover my changes.
    • All new and existing tests passed.
    • I have checked that this change does not introduce memory leaks.
    • I have used Valgrind or similar tools to check for potential issues.
  • I have given due consideration to all of the following potential concerns:
    • Scalability
    • Performance
    • Security
    • Thread-safety
    • Premature optimization
    • Existing deployed queries will not be broken
    • This change fixes the problem, not just the symptom
    • The target branch of this pull request is appropriate for such a change.
  • There are no similar instances of the same problem that should be addressed
    • I have addressed them here
    • I have raised JIRA issues to address them separately
  • This is a user interface / front-end modification
    • I have tested my changes in multiple modern browsers
    • The component(s) render as expected

Testing:

Run through hthor regression suite with forceRemotePattern on for all hthor files.

@hpcc-jirabot
Copy link

@jakesmith
Copy link
Member Author

NB: ValueSetTest::testKeyed2, ValueSetTest::testFilter, ValueSetTest::testKeyed1 fail at the moment, e.g. testKeyed(rows, record, "f1=[5]") fails in deserializeFieldFilter.
I think this is related to changes Gavin made to the filter format (?) - @ghalliday - can you check/comment?

@mckellyln @ghalliday @richardkchapman - please review some/all of the changes.

@@ -1632,6 +1649,16 @@ void RowFilter::addFilter(const IFieldFilter & filter)
numFieldsRequired = fieldNum+1;
}

void RowFilter::addFilter(const RtlRecord & record, const char * filterText)
{
//assertex(filter.queryField() == filters.ordinality()); //MORE - fill with wild filters and replace existing wild
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment still accurate? Is there a plan to address it? A Jira? Should it be fixed in this PR?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comment can be deleted - it was related to when they are used for keys, and that needs more work anyway.

Copy link
Member

@ghalliday ghalliday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A first review.
Main comments are to do with the remote activity implementation - particularly group handling.

if (cmd > RFCmaxnormal)
cmd = RFCmaxnormal;
unsigned elems = sizeof(RFCStrings) / sizeof(RFCStrings[0]);
if (cmd >= (sizeof(RFCStrings) / sizeof(RFCStrings[0])))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you meant to use elems

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. I'll change.

cursorInfo.append("\"\n");
mrequest.append(cursorInfo.length(), cursorInfo.str());
}
DBGLOG("req = <%s}>", request.str());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be less frequent - the logging may get excessive.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I'll make it based on dafilesrv conditional tracing level.

mutable bool eogPending = false;
mutable bool someInGroup = false;
const RtlRecord *record = nullptr;
RtlDynRow *filterRow = nullptr;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this probably leaks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, will add delete to dtor.

const byte *next = prefetchBuffer.queryRow();
bool eog = false;
if (inputGrouped)
prefetchBuffer.read(sizeof(eog), &eog);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could invalidate next. Need to call queryRow() after this line.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, will change.

bool eog = false;
if (inputGrouped)
prefetchBuffer.read(sizeof(eog), &eog);
prefetchBuffer.finishedRow();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could also invalidate next, needs to happen after the transform

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, will change.

if (!filterRow)
filterRow = new RtlDynRow(*record);
}
// IRemoteActivity impl.
virtual const void *nextRow(size32_t &retSz) override
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should really take outBuilder as a parameter - otherwise it is very confusing that the buffer is updated as a side-effect of the call.

for (unsigned __int64 i=0; i<defaultDaFSNumRecs; i++)
{
bool *eog;
if (grouped && outputActivity->queryProcessed()) // i.e. not 1st row
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect this logic to be executed after reading enough rows (since returning null is the normal way of indicating end of group), rather than each row.

Using a local eog variable appended to the buffer at the correct point.

size32_t rowSz;
const void *row = outputActivity->nextRow(rowSz);
const void *row = outputActivity->nextRow(rowSz); // NB: row builder writes directly to reply buffer for effiency
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This potentially invalidates eog pointer - so could corrupt memory.

(E.g., if a complex row is then filtered, followed by end of group).

break;
if (grouped)
{
if (outputActivity->queryProcessed()) // i.e. not 1st row
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a strange test to see if it is the first row - I would expect testing a local variable e.g., i != 0 or

virtual void serializeCursor(MemoryBuffer &tgt) const override
{
tgt.append(prefetchBuffer.tell());
tgt.append(processed);
tgt.append(someInGroup);
tgt.append(eogPending);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible for eogPending to ever be true?

@ghalliday
Copy link
Member

@jakesmith some initial comments

@jakesmith
Copy link
Member Author

forgot to tag you @ghalliday

Copy link
Member

@ghalliday ghalliday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very close, a few comments.

}
virtual unsigned __int64 getStatistic(StatisticKind kind)
{
UNIMPLEMENTED;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this cause problems? Should it be implemented in some way in a later PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should at least return 0, as other implementations do that don't return stats.

responseWriter->outputBeginNested("Row", true);
out->toXML((const byte *)row, *responseWriter);
responseWriter->outputEndNested("Row");
pastFirstRow = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth resetting outBuilder buffer, so it doesn't keep growing (and in the else below)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yes, well spotted.

@@ -1632,6 +1649,16 @@ void RowFilter::addFilter(const IFieldFilter & filter)
numFieldsRequired = fieldNum+1;
}

void RowFilter::addFilter(const RtlRecord & record, const char * filterText)
{
//assertex(filter.queryField() == filters.ordinality()); //MORE - fill with wild filters and replace existing wild
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comment can be deleted - it was related to when they are used for keys, and that needs more work anyway.

checkOpen();
if (needTransform)
while (!eofSeen && ((chooseN == 0) || (processed < chooseN)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure the chooseN == 0 check is correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it was code copied from hthor and matches thor behaviour too.
But it turns out 0 should mean 0 and not all as it is currently implemented here, in hthor and thor.
The behaviour was changed some years ago, but it looks like hthor and thor were never changed, however they get away with it, because the codegen always generates a subsequent choosen activity.
I've opened a separate JIRA to change hthor and thor choosen==0 behaviour : https://track.hpccsystems.com/browse/HPCC-19440

inputfile.setown(createIFile(rfilename));
if(compressed)
inputfile.setown(createIFile(rfilename));
if (rfilename.isLocal() && (!canSerialize || !readRemote(rfilename)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this test correct?
Currently xml/csv needs to be read local - even if it is not a local filename
Similarly if you cannot serialize then it needs to be local.

Possibly:
if (!isBinary() || !canSerialize || (isLocal && !readRemote())

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, also going to change readRemote() to forceRemote() for clarity.

calcFixedDiskRecordSize();
actualFilter.appendFilters(fieldFilters);

bool canSerialize = actualDiskMeta->queryTypeInfo()->canSerialize() && projectedDiskMeta->queryTypeInfo()->canSerialize();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: canSerializeTypeInfo would be a better indication of what couldn't be serialized.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will change.

@ghalliday
Copy link
Member

@jakesmith

@jakesmith
Copy link
Member Author

@ghalliday - please see changes following last review.

@HPCCSmoketest
Copy link
Contributor

Automated Smoketest: ✅
OS: centos 7.2.1511 (Linux 3.10.0-327.28.3.el7.x86_64)
Sha: b46bda9
Build: success
Install hpccsystems-platform-community_6.5.0-trunk0.el7.x86_64.rpm
HPCC Start: OK

Unit tests result:

Test total passed failed errors timeout
unittest 84 84 0 0 0
wutoolTest(Dali) 19 19 0 0 0
wutoolTest(Cassandra) 19 19 0 0 0

Regression test result:

phase total pass fail
setup (hthor) 11 11 0
setup (thor) 11 11 0
setup (roxie) 11 11 0
test (hthor) 759 759 0
test (thor) 686 686 0
test (roxie) 799 799 0

HPCC Stop: OK
HPCC Uninstall: OK
Time stats:

Prep time Build time Package time Install time Start time Test time Stop time Summary
17 sec (00:00:17) 182 sec (00:03:02) 56 sec (00:00:56) 6 sec (00:00:06) 22 sec (00:00:22) 1105 sec (00:18:25) 16 sec (00:00:16) 1404 sec (00:23:24)

Implementation of a streaming remote project/filtering disk i/o
class for use by engines, plus initial use by hthor.

NB: This commit contains various fixes to rtl, hthor, dafilesrv
discovered/required during implementation.
It also adds:
1) remote compression support
2) remote filtering
3) remote grouped file support
4) Tidies up the dafilesrv exception handling and improves
the error messages fed back to the client.

Signed-off-by: Jake Smith <jake.smith@lexisnexisrisk.com>
@ghalliday ghalliday merged commit 5a00c4e into hpcc-systems:master Apr 11, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants