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

Chaos testing module #130

Merged
merged 5 commits into from Jun 22, 2018
Merged

Chaos testing module #130

merged 5 commits into from Jun 22, 2018

Conversation

IgorPerikov
Copy link
Contributor

According to #118
That's what I came up with - a draft implementation of latency injection filter

I also added header setting, so one can understand whether the request is slow because of chaos injection or some other reasons

cc @daviddenton

@coveralls
Copy link

coveralls commented May 23, 2018

Coverage Status

Coverage decreased (-0.3%) to 92.425% when pulling 124c552 on IgorPerikov:#118 into 9fcb431 on http4k:master.

@IgorPerikov
Copy link
Contributor Author

about failed checks:

  • 1 introduced from codebeat - duplicate code, it's ok since it's a draft to show what I had in mind
  • coverage decreased - same, one of the filters is not covered
  • travis docs section failed - I don't understand yet what this about, I guess I'll figure out later on with that

@daviddenton
Copy link
Member

@IgorPerikov Don't worry about the docs thing - it's a known issue that the build attempts to perform the build docs step on the PR branches. We've tried to fix that a couple of times already but it seems to be elusive! Haven't had a chance to look over your code yet - will try to have a look soon. :)

@daviddenton
Copy link
Member

Obviously this is still WIP, but a couple of things about how we would envisage this going. Overall you're on the right track - just pointers really - not criticism! :) :

  1. Like the idea of SysEnv instead of sysprops - more 12 factor app etc.. :)

  2. We'd want this stuff be a new module - maybe http4k-testing-chaos?. Obviously that's a bit of work to add in all the right places, but you can follow the examples for something simple like the webdriver module - just search for occurances of webdriver and go from there. :)

  3. It would be nice to separate the algorithm for injecting random behaviour (maybe a ChaosPolicy typealias/interface?), away from the Filters injecting that behaviour. This would accomplish 2 things: leaning on delegation allows users to develop their own policies/behaviours (one of which is the system environmental variable option that you've already come up with), and it makes things easier to test (rather than conflating the randomisation and the behavioural effect). So taking this further, you'd probably get 3 overall concepts:
    a. The "ChaosPolicy" to define a couple of predicates which determine if a behaviour should be applied for a request or response (this allows you to do it before or after the downstream call). One of the policy implementations (the default) would be to never apply any behaviours.
    b. "Behaviours" - these apply the actual behaviour - e.g latency or blowing up etc.
    c. A filter which coordinates #a and #b - so it checks to see if a behaviour should be activated for a request (or a response).

  4. With regards to the environmental version, we generally try to lean on simple domain types rather than primitives. so instead of using an int, you'd use a Duration and get the millisecond delay from that. This is obviously more of a challenge (to deserialise from environmental vars etc), but we can also use the policy idea from Work on refactoring lens #3 to develop this in isolation from the behaviours so that can be done later :)

That was a bit of a braindump as well - if it doesn't make sense then just yell. :)

@IgorPerikov
Copy link
Contributor Author

if it doesn't make sense then just yell. :)

the points you mentioned are really valuable, I agree with all of them, I'll get back with a new stuff later

@IgorPerikov
Copy link
Contributor Author

A few things I'd like to emphasize here:

  • I'm not feeling good about ChaosBehavioursTest, it's really clumsy, especially the one that checks delay, but I decided to show what is the best thing I came up with
  • But what is more clumsy is potential test for chaos policies, especially randomized one, I decided not to write test here at all, since it involves a lot of stuff like making a lot of iterations, calculating result distribution, picking right epsilon since nothing is ideal, etc.

@IgorPerikov IgorPerikov changed the title Latency injection filter Chaos testing module May 30, 2018
@IgorPerikov
Copy link
Contributor Author

@daviddenton kindly ping?

@@ -65,5 +65,6 @@ maven_publish "http4k-server-jetty"
maven_publish "http4k-server-netty"
maven_publish "http4k-server-undertow"
maven_publish "http4k-serverless-lambda"
maven_publish "http4k-testing-chaos"
Copy link
Member

Choose a reason for hiding this comment

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

For the moment we need to remove this line from the commit - because there is a cycle for when we create a new module - it needs to be uploaded to bintray, then we have to ask permission for it to be linked to JCenter, and only then can we sync it to maven. If we add this line now, it'll fail build that we release it in! :)

{ request ->
if (chaosPolicy.shouldInject(request)) {
behaviour.inject(request)
next(request).header(LATENCY_HEADER, behaviour.javaClass.name)
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 no longer LATENCY only - just call it X-http4k-chaos or similar. We can also add the description of the behaviour as a method on the behaviour instance, and then use it to generate dynamic names based on what we did! :)

const val LATENCY_HEADER = "X-Chaos-Behaviour"

object ChaosFilters {
object ChaosPreFilter {
Copy link
Member

Choose a reason for hiding this comment

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

Can probably combine these into a single filter and then have 2 methods on the Policy shouldInject(Request) and shouldInject(Response) - both of which can default to false.

@daviddenton
Copy link
Member

@IgorPerikov sorry for going quiet - given you comments I wasn't sure if you were still actively committing to the branch so left you to it! :)

Anyway - it's a good start - I've left a couple of comments - the other thing that might help to drive out an API (specifically around the configuration) is to introduce further behaviours now:

  1. Kill the process!
  2. Block the current thread entirely (thread starvation)
  3. Send back an HTTP 500.

* merge pre and post filters into single one
* add inject(Response) & inject(Request) methods to behaviour interface
* provide more chaos behaviours
@IgorPerikov
Copy link
Contributor Author

@daviddenton new stuff is ready to review!

When I was working under it, I realised, that it would be extremely conventional to have two methods for ChaosBehaviour as well - one for request and one for response

latest changelist:

  • merge pre and post filters into single one
  • add inject(Response) & inject(Request) methods to behaviour interface
  • provide more chaos behaviours

@@ -48,5 +48,6 @@ maven_publish "http4k-server-jetty"
maven_publish "http4k-server-netty"
maven_publish "http4k-server-undertow"
maven_publish "http4k-serverless-lambda"
maven_publish "http4k-testing-chaos"
Copy link
Member

Choose a reason for hiding this comment

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

this needs to be removed as we won't publish the module until it's in jcenter..

}

class ChaosException(message: String) : Exception(message)

Copy link
Member

Choose a reason for hiding this comment

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

generally, the pattern within the project is for this kind of thing to appear as an extension function of the parent type. So, for instance fun ChaosBehaviour.ThrowException(). This one in particular could take an exception as an arg, and default to throw ChaosException as the default.

import org.http4k.core.Response
import java.util.concurrent.ThreadLocalRandom

interface ChaosPolicy {
Copy link
Member

Choose a reason for hiding this comment

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

ditto the remark about extension functions (from above)

Copy link
Member

@daviddenton daviddenton left a comment

Choose a reason for hiding this comment

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

Have a look at my suggestions and let me know what you think :)

@IgorPerikov
Copy link
Contributor Author

let me know what you think

sure I will follow the project rules, but tbh I'm not really following this design principle - not a kotlin native speaker yet :). Is this what are you talking here?

class ExceptionThrowingBehaviour : ChaosBehaviour {
    override val description = "Exception"
    override fun inject(response: Response) = throwException()
}

fun ChaosBehaviour.throwException(exception: Exception = ChaosException("Chaos behaviour injected!")): Response {
    throw exception
}

I guess my assumption is wrong, since it seems really weird for me, but I decided to show it just in case :)

At first I thought about moving all the subtypes to be a companion object functions(pretty much like ContentNegotiation interface in body.kt), and it seems more convenient for me, but AFAIK it has nothing in common with extension functions 😮

interface ChaosBehaviour {
    ... // signatures omitted for brevity
    companion object {
        fun throwException(exception: Exception = ChaosException("Chaos behaviour injected!")) = object : ChaosBehaviour {
            override val description = "Exception"

            override fun inject(response: Response): Response {
                throw exception
            }
        }
    }
}

In addition to explanation, I'd be really grateful, if you could point me a few places in codebase where I could find an examples of this pattern for better understanding

@daviddenton

@daviddenton
Copy link
Member

Hi @IgorPerikov - first off, I'm really, really sorry for the slow turnaround in this - I've been working up against a deadline so am completely flooded at the moment. It should be better by the end of next week so bear with me! Startups are hard! :)

And whilst I'm waiting for a build to run....

Your intuition was completely right about ContentNegotiation being the template for what I meant - the companion object of the ChaosBehaviour interface would be where we would put these core behaviours. Then further behaviours can be added as extension methods TO the companion object, and hence we keep it all nice and discoverable in the IDE.

This also means that you can drop the name "Behaviour" from each of the implementing classes, so you'd have: ChaosBehaviour.ThrowException**. I'd also maybe give the API use the option to use their own exception and keep the ChaosException as a default parameter in the function

** note the capitalization here - we follow a pattern where you can't really tell if a function is a constructor OR a class. This means that if we move from a function to a class at a later date, it doesn't break the API for the user.

@IgorPerikov
Copy link
Contributor Author

Hi, no need for apologize at all, no matter what is the reason. Thanks for clarification - clear and neat, will come back with update when it's ready

@IgorPerikov
Copy link
Contributor Author

seems like I am already back :) don't hesitate to mark/correct names in the code - I got used to them plus english is not my mother tongue

@daviddenton
Copy link
Member

Sorry (again) - I totally missed your message that you were done. Good news is that stupid deadline is done, so I can start to concentrate on other things! :)

@daviddenton daviddenton merged commit a5ef09f into http4k:master Jun 22, 2018
@IgorPerikov
Copy link
Contributor Author

Glad that you meet your deadline! 🎆

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.

None yet

3 participants