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

Activation id in header #3671

Merged
merged 4 commits into from Aug 22, 2018
Merged

Conversation

style95
Copy link
Member

@style95 style95 commented May 18, 2018

This closes #3582

Description

This change will add activation id in the response headers.
Since same logic needs to be used in both collection API and web action API, I created dedicated trait for custom headers.

Related issue and scope

My changes affect the following components

  • API
  • Controller
  • Message Bus (e.g., Kafka)
  • Loadbalancer
  • Invoker
  • Intrinsic actions (e.g., sequences, conductors)
  • Data stores (e.g., CouchDB)
  • Tests
  • Deployment
  • CLI
  • General tooling
  • Documentation

Types of changes

  • Bug fix (generally a non-breaking change which closes an issue).
  • Enhancement or new feature (adds new functionality).
  • Breaking change (a bug fix or enhancement which changes existing behavior).

Checklist:

  • I signed an Apache CLA.
  • I reviewed the style guides and followed the recommendations (Travis CI will check :).
  • I added tests to cover my changes.
  • My changes require further changes to the documentation.
  • I updated the documentation where necessary.

Copy link
Member

@rabbah rabbah left a comment

Choose a reason for hiding this comment

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

Rather than add new tests, how about adding a header check to existing tests for invokes, and triggers.


respondWithActivationIdHeader(activationId) {
// note that if header defined a content-type, it will be ignored
// since the type must be compatible with the data response
Copy link
Member

Choose a reason for hiding this comment

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

I don’t think this comment makes sense here.

Copy link
Member Author

Choose a reason for hiding this comment

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

removed.

@@ -634,37 +634,40 @@ trait WhiskWebActionsApi extends Directives with ValidateRequestSize with PostAc
responseType: MediaExtension)(implicit transid: TransactionId) = {
onComplete(queuedActivation) {
case Success(Right(activation)) =>
val result = activation.resultAsJson
respondWithActivationIdHeader(activation) {
Copy link
Member

Choose a reason for hiding this comment

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

I do not think the activation id should be reported in webaction. It leaks information. For normal POSTS it is ok because there’s an explicit subject key. For webactions, there is not.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is to cope with the inconvenience which is described in #3582.
This will give users a better option to retrieve right activation results.

Copy link
Member

@rabbah rabbah May 24, 2018

Choose a reason for hiding this comment

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

A webaction is called by an anonymous subject. The activation id is information specific to the subject owning the action.

The webaction can return its own header to identify the action; which could be the activation id if it chooses - I do not think the system should blanket return the activation id for webactions.

@style95
Copy link
Member Author

style95 commented May 24, 2018

@rabbah
Activation id header checking is added in existing tests.

protected trait CustomHeaders extends Directives {

/** Add activation ID in headers */
protected def respondWithActivationIdHeader(activation: WhiskActivation): Directive0 =
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to override this method?
Wouldn't it be easier to always pass the activationId? You have it anyway, if you have the activation.

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated accordingly

@@ -91,6 +91,8 @@ protected trait ControllerTestCommon
val authStore = WhiskAuthStore.datastore()
val logStore = SpiLoader.get[LogStoreProvider].logStore(actorSystem)

val ActivationIdHeaderInLowercase = "x-openwhisk-activation-id"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you move this variable to CustomHeaders and use it here as well? (And start with a lowercase letter)

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated accordingly.

Regarding case, I followed scala constant naming convention(Upper camel case).
Is it OW convention(start with a lowercase letter)?

@ddragosd
Copy link
Contributor

Given that the API Gateway sets the X-Request-Id as per #3199 , wouldn't it be easier to have the Gateway add it to the response header as well ?

@rabbah
Copy link
Member

rabbah commented May 24, 2018

@ddragosd the request id isn't the activation id though --- although I raised the question in the related issue: should the two be the same in which case, the gateway or nginx generates the id and adds the header on the response.

@ddragosd
Copy link
Contributor

should the two be the same

Any reason they can't be the same ?

@rabbah
Copy link
Member

rabbah commented May 24, 2018

activation id needs to be unique to a namespace since it serves as part of the document id stored in the activation store (for metadata etc)... if that's satisfied i think we can do it.

@ddragosd
Copy link
Contributor

makes sense @rabbah . I'd say we can safely assume that the Gateway generates unique activation IDs.

@cbickel
Copy link
Contributor

cbickel commented May 25, 2018

@rabbah @ddragosd
I also thought about making tid and activationid the same, but I see the following problems:

  • If a trigger is invoked, you have a trigger activation and several action activations. Which of these activations should get the tid as activation id
  • Same for sequences. Which of the actions should get the tid as activation-id.
  • One of the ideas of sending the tid to the controller, was to generate the tid in one of the providers and pass it with the request. With this ability you would be able to trace a request from end to end. But this also adds the risk, that it could not be unique anymore.

But maybe there is an easy solution for all of these concerns ;)

@codecov-io
Copy link

codecov-io commented May 25, 2018

Codecov Report

Merging #3671 into master will decrease coverage by 4.67%.
The diff coverage is 96.42%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3671      +/-   ##
==========================================
- Coverage   85.43%   80.75%   -4.68%     
==========================================
  Files         147      147              
  Lines        7070     7077       +7     
  Branches      423      423              
==========================================
- Hits         6040     5715     -325     
- Misses       1030     1362     +332
Impacted Files Coverage Δ
...rc/main/scala/whisk/core/controller/Triggers.scala 94.24% <100%> (+0.04%) ⬆️
...rc/main/scala/whisk/core/controller/Entities.scala 93.54% <100%> (+0.44%) ⬆️
...src/main/scala/whisk/core/controller/Actions.scala 92.96% <100%> (+0.07%) ⬆️
.../main/scala/whisk/core/controller/WebActions.scala 91.15% <92.85%> (+0.06%) ⬆️
...core/database/cosmosdb/RxObservableImplicits.scala 0% <0%> (-100%) ⬇️
...core/database/cosmosdb/CosmosDBArtifactStore.scala 0% <0%> (-95.1%) ⬇️
...sk/core/database/cosmosdb/CosmosDBViewMapper.scala 0% <0%> (-92.6%) ⬇️
...whisk/core/database/cosmosdb/CosmosDBSupport.scala 0% <0%> (-81.82%) ⬇️
...abase/cosmosdb/CosmosDBArtifactStoreProvider.scala 0% <0%> (-58.83%) ⬇️
...la/whisk/core/database/cosmosdb/CosmosDBUtil.scala 92% <0%> (-4%) ⬇️
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e414b33...ec20733. Read the comment docs.

@rabbah
Copy link
Member

rabbah commented May 25, 2018

For the first parts:

For a trigger, the request id is the triggers activation id. Every activated action generates a new activation id as we do today.

For a sequence the outermost action (the sequence itself) gets the id all the subcomponents get new ids (as today).

Same for conductor actions.

The uniqueness is the part I’m concerned about.

@style95
Copy link
Member Author

style95 commented May 28, 2018

Let me recap the intermediate conclusion.

  1. Activation ID should not be exposed by the system instead, users can decide to whether expose it or not using existing activation id parameter in case the action is a webaction.
  2. Use request ID generated by nginx or gateway as Activation ID as well.

Regarding 1, I initially raised this issue in the aspect of UI. When providing the functionality to display activation results and logs after an invocation of an action in UI, it's not easy to select right activation when the action is webaction.

In case of normal action also, since activation logs are collected asynchronously, it's not that simple.
But anyway we can look it up because it returns activation ID in the response.

For webactions, it's not easy to choose a specific activation, and display logs for web actions without system support.

Any suggestions for this?

With regard to 2, so request ID would be Transaction ID and Activation ID.
More precisely Transaction id will be same with activation id. Would it be ok?

I just want to confirm.
As @ddragosd said if we can safely assume it is unique, would it be ok for this PR to take that in?

@rabbah
Copy link
Member

rabbah commented Jun 11, 2018

@cbickel does #3671 (comment) address your concerns?

if we adopt the nginx generated id as the activation id, then this pr becomes a lot simpler.
we can also remove the double headers currently generated by the controller (and only emit it if an id isn't received).

@cbickel
Copy link
Contributor

cbickel commented Jun 11, 2018

@rabbah
Not completely. I agree, that this would be the best solution, but the problem is, that on firing a trigger, the controller is calling itself with the transaction-Id.
To my knowledge, the controller doesn't recognise today, that the action is part of a rule or a normal action. Or am I wrong?
And the uniqueness is still a problem. If the tids are not unique, it would be bad for debugging, but if activation-Ids are not unique anymore, users wont't get activation-records anymore.

@@ -1043,13 +1043,15 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
status should be(Accepted)
val response = responseAs[JsObject]
response.fields("activationId") should not be None
headers.exists(_.is(ActivationIdHeaderInLowercase)) should be(true)
Copy link
Contributor

Choose a reason for hiding this comment

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

headers are immutable and per the line above you know the correct activation id. You should be able to rewrite this to something like:

headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))

That'll give you a more explanatory response in tests. WDYT?

headers.exists(header => {
header.is("set-cookie") && header.value() == "a=b"
}) shouldBe true
headers.exists(_.is(ActivationIdHeaderInLowercase)) shouldBe true
Copy link
Contributor

Choose a reason for hiding this comment

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

See comment above, please rewrite both of these into "contains" checks.

Copy link
Member Author

Choose a reason for hiding this comment

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

got it

}

// repeat invoke, get only result back
Post(s"$collectionPath/${action.name}?blocking=true&result=true") ~> Route.seal(routes(creds)) ~> check {
headers
Copy link
Contributor

Choose a reason for hiding this comment

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

bad push?

@markusthoemmes
Copy link
Contributor

Is there something actionable in here? Should we take the discussion out to the dev-list? At least the bit on generating activationIds from something outside of the controller seems worth discussing in a broader audience.

@style95
Copy link
Member Author

style95 commented Aug 8, 2018

I think the point is, if we allow activationID generation in multiple components(nginx, controller), we cannot guarantee the activationID is unique(though the possibility of duplicate activationID is less.)

To use the same ID for TransactionID and ActivationID, it should be generated in nginx.
But also, ActivationID should be generated in controllers as a trigger is fired by controllers.

@style95
Copy link
Member Author

style95 commented Aug 8, 2018

@rabbah
Regarding exposure of ActivationID of webaction in response header, how about having a configuration to control it by OW operator?
In some cases, it would be worth to expose it though it could be a kind of small data breaches.

@rabbah
Copy link
Member

rabbah commented Aug 8, 2018

I think I've changed perspective on the activation id in the header response of web actions, after some further consideration (to agree with you, and that it's OK). I can explain in a longer comment later if necessary.

@style95
Copy link
Member Author

style95 commented Aug 14, 2018

How about separating ActivationID addition in headers and using same ID for TransactionID and ActivationID?
I think there is not big correleation between the two.

How about taking these changes and discussing the second one in dev-list or another PR separately?

}

// repeat this time wait longer than active ack delay
Post(s"$collectionPath/${action.name}?blocking=true&timeout=500") ~> Route.seal(routes(creds)) ~> check {
status shouldBe OK
val response = responseAs[JsObject]
response shouldBe activation.withoutLogs.toExtendedJson
headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))

Copy link
Member

Choose a reason for hiding this comment

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

Extra line.

@@ -1319,6 +1329,8 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
status should be(InternalServerError)
val response = responseAs[JsObject]
response should be(activation.withoutLogs.toExtendedJson)
headers should contain(RawHeader(ActivationIdHeader, response.fields("activationId").convertTo[String]))

Copy link
Member

Choose a reason for hiding this comment

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

Extra line.

@@ -89,6 +89,8 @@ protected trait ControllerTestCommon
val logStore = SpiLoader.get[LogStoreProvider].instance(actorSystem)
val activationStore = SpiLoader.get[ActivationStoreProvider].instance(actorSystem, materializer, logging)

val ActivationIdHeaderInLowercase = ActivationIdHeader.toLowerCase

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 used?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it would be better to keep it because I restored the previous exist logic.
Unlike a normal action, we are unable to get the value of ActivationID from the response as so unable to use contain logic that Markus suggested.

headers should contain(RawHeader(ActivationIdHeader, _)) // does not work

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd propose to make the global setting lowercased then, and use that throughout. WDYT?

Copy link
Member Author

@style95 style95 Aug 16, 2018

Choose a reason for hiding this comment

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

Sorry, what is the global setting?

Copy link
Member Author

@style95 style95 Aug 16, 2018

Choose a reason for hiding this comment

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

The value in CustomHeaders?

@style95 style95 force-pushed the activation-id-in-header branch 2 times, most recently from 6d83f8d to 8d9608e Compare August 16, 2018 02:09
@style95
Copy link
Member Author

style95 commented Aug 16, 2018

Travis build has passed.

@style95 style95 closed this Aug 17, 2018
@style95 style95 reopened this Aug 17, 2018
@style95
Copy link
Member Author

style95 commented Aug 17, 2018

Updated accordingly

@@ -65,15 +65,24 @@ protected[controller] trait ValidateRequestSize extends Directives {
protected val fieldDescriptionForSizeError = "Request"
}

protected trait CustomHeaders extends Directives {
val ActivationIdHeader = "x-openwhisk-activation-id"
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 @mhenke1 introduced a header with X-OW as the prefix. API gateway uses x-openwhisk. We should normalize and collocate these. Fwiw nginx also generates x-request-id header.

Copy link
Member Author

Choose a reason for hiding this comment

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

@rabbah Agree.
How about using x-openwhisk?
Since x- header is a custom header specific to the application, it would be better to use the detailed one.

Copy link
Member Author

Choose a reason for hiding this comment

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

I am not sure changes to use the same OpenWhisk prefix for all custom headers should be included in this PR.

@style95
Copy link
Member Author

style95 commented Aug 22, 2018

Any comments on this?

@rabbah
Copy link
Member

rabbah commented Aug 22, 2018

I think we should still pursue id consolidation - I don't think we need both an activation id and a transaction id for top level activations, and we should further then reduce the number of headers we're communicating back (it would just be one id that means both). Deferring to another discussion/PR.

@rabbah rabbah merged commit 75472f8 into apache:master Aug 22, 2018
@rabbah
Copy link
Member

rabbah commented Jan 11, 2019

I used this today. 💪

@style95
Copy link
Member Author

style95 commented Jan 11, 2019

Sounds great to me :)

BillZong pushed a commit to BillZong/openwhisk that referenced this pull request Nov 18, 2019
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.

[Feature Request] Include activation ID in web action header.
6 participants