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-31755 Soapcall LOG multi-line separator #19145

Open
wants to merge 2 commits into
base: candidate-9.6.x
Choose a base branch
from

Conversation

mckellyln
Copy link
Contributor

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
    • Cloud-compatibility
    • 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

Smoketest:

  • Send notifications about my Pull Request position in Smoketest queue.
  • Test my draft Pull Request.

Testing:

Copy link

Jira Issue: https://hpccsystems.atlassian.net//browse/HPCC-31755

Jirabot Action Result:
Workflow Transition To: Merge Pending
Additional PR: #19145

Copy link
Member

@jakesmith jakesmith left a comment

Choose a reason for hiding this comment

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

@mckellyln - please see comments.

@@ -1951,10 +1952,19 @@ class CWSCAsyncFor : implements IWSCAsyncFor, public CInterface, public CAsyncFo
{
if (soapTraceLevel > 6 || master->logXML)
{
if (!contentEncoded)
master->logctx.mCTXLOG("%s: request(%s)", master->wscCallTypeText(), request.str());
StringBuffer contentStr("");
Copy link
Member

Choose a reason for hiding this comment

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

trivial/style: ("") not needed.

StringBufferAdaptor soapSepAdaptor(soapSepStr);
agent.queryWorkUnit()->getDebugValue("soapLogSepString", soapSepAdaptor);
if (!soapSepStr.isEmpty())
soapSepString.clear().append(soapSepStr);
Copy link
Member

Choose a reason for hiding this comment

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

why not populate soapSepStr directly? :

    StringBufferAdaptor soapSepAdaptor(soapSepString);
    agent.queryWorkUnit()->getDebugValue("soapLogSepString", soapSepAdaptor);

Copy link
Member

Choose a reason for hiding this comment

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

calling here (in activity init code) means it this will be called multiple times per job (if there are act based on this base per job). [ that's true of existing soapTraceLevel line too ]

Maybe nothing to worry about

@@ -50,6 +50,7 @@ using roxiemem::OwnedRoxieString;
#define CONNECTION "Connection"

unsigned soapTraceLevel = 1;
StringBuffer soapSepString("");
Copy link
Member

Choose a reason for hiding this comment

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

trivial/style: ("") not needed.

request2.replaceString("\r\n", soapSepString.str());
request2.replaceString("\r", soapSepString.str());
request2.replaceString("\n", soapSepString.str());
master->logctx.mCTXLOG("%s: request(%s)%s", master->wscCallTypeText(), request2.str(), contentStr.str());
Copy link
Member

Choose a reason for hiding this comment

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

minor: the start of contentStr could be commoned up to include the "%s: request":

VStringBuffer contentStr("%s: request", master->wscCallTypeText());

@@ -86,5 +86,6 @@ interface IRoxieAbortMonitor
extern THORHELPER_API unsigned soapTraceLevel;
extern THORHELPER_API IWSCHelper * createSoapCallHelper(IWSCRowProvider *, IEngineRowAllocator * outputAllocator, const char *authToken, SoapCallMode scMode, ClientCertificate *clientCert, const IContextLogger &logctx, IRoxieAbortMonitor * roxieAbortMonitor);
extern THORHELPER_API IWSCHelper * createHttpCallHelper(IWSCRowProvider *, IEngineRowAllocator * outputAllocator, const char *authToken, SoapCallMode scMode, ClientCertificate *clientCert, const IContextLogger &logctx, IRoxieAbortMonitor * roxieAbortMonitor);
extern THORHELPER_API StringBuffer soapSepString;
Copy link
Member

Choose a reason for hiding this comment

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

this is going to cause the object to be copied where included I think.
Should either be a reference or a method
To be reference would mean the cpp would have to have code something like this:

static StringBuffer _soapSepString;
StringBuffer &soapSepString = _soapSepString;

A method is probably cleanest.

response2.replaceString("\r\n", soapSepString.str());
response2.replaceString("\r", soapSepString.str());
response2.replaceString("\n", soapSepString.str());
master->logctx.mCTXLOG("%s: LEN=%d %sresponse(%s)", getWsCallTypeName(master->wscType),response.length(),chunked?"CHUNKED ":"", response2.str());
Copy link
Member

Choose a reason for hiding this comment

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

mCTXLOG (multiline CTXLOG) is parsing newline/carriage returns specifically to call multiple CTXLOG calls).
I think you should be calling logctx.CTXLOG after removing the newlines.

else if (stricmp(queryName, "control:soapLogSepString")==0)
{
if (control->hasProp("@string"))
control->getProp("@string", soapSepString);
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 theoretically thread unsafe in combination with the placement of the code in HThorWSCBaseActivity::init() ?
e.g. if an activity is initializing when this thread is also initializing.

Also, if called multiple times soapSepString will grow (should be soapSepString.clear())

response2.append(response);
response2.replaceString("\r\n", soapSepString.str());
response2.replaceString("\r", soapSepString.str());
response2.replaceString("\n", soapSepString.str());
Copy link
Member

Choose a reason for hiding this comment

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

Quite expensive (to build up new StringBuffer with dbgheader+response), then call multiple replaceString's which will walk the whole response for each call.

It may be worth adding a function to append to a StringBuffer replacing the sequences with the new separator (or removing them) as it goes.
An efficient implementation could be quite simple if it only removed (meaning the target was guaranteed to be smaller than source).

Copy link
Member

Choose a reason for hiding this comment

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

NOTE this PR to optimize replaceString: #19187

@mckellyln
Copy link
Contributor Author

@jakesmith thx, please re-review changes in commit 2

}

static void multiLineAppendReplace(StringBuffer &origStr, StringBuffer &newStr)
{
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As an opt, we could index for '\r' and then append chunks between newlines.

Copy link
Member

Choose a reason for hiding this comment

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

yes, if it was very time critical, might be worth two pass, so can allocate correct size once, reserve and use regular ptr's instead of calls to StringBuffer, but doubt worth it.

Copy link
Member

@jakesmith jakesmith left a comment

Choose a reason for hiding this comment

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

@mckellyln - looks good, a few minor/trivial comments

StringBuffer soapSepString;
static CriticalSection soapCrit;

void setSoapSepString(StringBuffer &_soapSepString)
Copy link
Member

Choose a reason for hiding this comment

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

trivial/style: if no benefit to passing a StringBuffer as param vs simlp[e const char *, then we normally do later, it's more portable/usable (doesn't enforce having to need a StringBuffer when not needed).

void setSoapSepString(StringBuffer &_soapSepString)
{
CriticalBlock b(soapCrit);
soapSepString.clear().append(_soapSepString);
Copy link
Member

Choose a reason for hiding this comment

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

trivial: StringBuffer::set is equivalent

@@ -7741,8 +7741,7 @@ void CHThorWSCBaseActivity::init()
StringBuffer soapSepStr;
StringBufferAdaptor soapSepAdaptor(soapSepStr);
agent.queryWorkUnit()->getDebugValue("soapLogSepString", soapSepAdaptor);
if (!soapSepStr.isEmpty())
soapSepString.clear().append(soapSepStr);
setSoapSepString(soapSepStr);
Copy link
Member

Choose a reason for hiding this comment

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

this means the default (and in thorsoapcall.cpp) is an empty string.
i.e. by default soap responses will be stripped of linefeeds and lines concatenated together. Is that what we want?
Shouldn't " " be set to default in thorsoapcall, and not changed here / elsewhere, unless option provided?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If soapSepString is empty (the default unless its set in config) then no translation is done.
I didn't think we should also allow only removal of the newlines.

getOpt(THOROPT_SOAP_LOG_SEP_STRING, soapSepString);
StringBuffer tmpSepString;
getOpt(THOROPT_SOAP_LOG_SEP_STRING, tmpSepString);
setSoapSepString(tmpSepString);
Copy link
Member

Choose a reason for hiding this comment

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

see other comment in hthor.cpp - shouldn't it default to something like " " in thorsoapcall.cpp - and only set/change here if property present? Otherwise default is to strip and join lines together.

@@ -50,6 +50,41 @@ using roxiemem::OwnedRoxieString;
#define CONNECTION "Connection"

unsigned soapTraceLevel = 1;
StringBuffer soapSepString;
Copy link
Member

Choose a reason for hiding this comment

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

looks like this should now be a static

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, yes

}

static void multiLineAppendReplace(StringBuffer &origStr, StringBuffer &newStr)
{
Copy link
Member

Choose a reason for hiding this comment

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

yes, if it was very time critical, might be worth two pass, so can allocate correct size once, reserve and use regular ptr's instead of calls to StringBuffer, but doubt worth it.

Copy link
Member

@jakesmith jakesmith left a comment

Choose a reason for hiding this comment

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

@mckellyln - looks good. Please squash.

@mckellyln
Copy link
Contributor Author

Thanks, squashed.

if (origStr.isEmpty())
return;
const char *cursor = origStr;
while (cursor)
Copy link
Member

Choose a reason for hiding this comment

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

should be while (*cursor) (or could be while (true) which it effectively is)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

static void multiLineAppendReplace(StringBuffer &origStr, StringBuffer &newStr)
{
if (origStr.isEmpty())
return;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Perhaps we newStr.ensureCapacity(origStr.length()) before starting loop so we dont have to expand it more than once ?

Copy link
Member

Choose a reason for hiding this comment

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

if replacement separator was >1 char and existing separator was \n, it would still have to expand more than once.
an initial guess ensureCapacity wouldn't harm, but not sure worth it, unless this is going to intensively used.

Copy link
Member

Choose a reason for hiding this comment

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

I agree preallocate to the original size.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added

@mckellyln
Copy link
Contributor Author

I don't understand failing test -

exec sudo snap install microk8s --channel=1.27/stable --classic { silent: false }
error: cannot perform the following tasks:

  • Download snap "microk8s" (7016) from channel "1.27/stable" (unexpected EOF)

I think this is unrelated to code changes ?

@mckellyln
Copy link
Contributor Author

ok, a rerun of the tests shows all ok

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 good, a few minor comments

StringBuffer contentStr;
if (contentEncoded)
contentStr.append(", content encoded.");
if ( (master->logXML) && (soapSepString.length() > 0) )
Copy link
Member

Choose a reason for hiding this comment

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

check: I think the master->logXML test can be deleted.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I left it and added comment - so we only do translation if LOG set in SOAPCALL args

static void multiLineAppendReplace(StringBuffer &origStr, StringBuffer &newStr)
{
if (origStr.isEmpty())
return;
Copy link
Member

Choose a reason for hiding this comment

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

I agree preallocate to the original size.

else if (soapTraceLevel > 8)
master->logctx.mCTXLOG("%s: LEN=%d %sresponse(%s)", getWsCallTypeName(master->wscType),response.length(),chunked?"CHUNKED ":"", response.str()); // not sure this is that useful but...
{
if ( (master->logXML) && (soapSepString.length() > 0) )
Copy link
Member

Choose a reason for hiding this comment

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

ditto


void setSoapSepString(const char *_soapSepString)
{
CriticalBlock b(soapCrit);
Copy link
Member

Choose a reason for hiding this comment

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

Remove the critical and the control: command to change it, and require that this is only called when no soapcalls are running, OR all access to this static needs to be in a critical block

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it enough that we remove the control: dynamic setting of this ? As then its only set at job start or Roxie start.

@mckellyln
Copy link
Contributor Author

@ghalliday updated PR, thank you. Will squash when ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants