Skip to content
This repository was archived by the owner on Jan 24, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4b397a7
diffy : returning 200 OK instead of NoResponseException for http serv…
puneetkhanduri Nov 25, 2015
815812c
Removing println
alexshadow007 Dec 29, 2015
712de36
fix compatibility with compose 1.5.0+
bjaglin Dec 23, 2015
da5764d
modified StartupFeatureTest to bind to port 0
puneetkhanduri Apr 26, 2016
3f3e2d0
Setting version to 0.0.2-SNAPSHOT
puneetkhanduri Apr 29, 2016
9fe7d1a
modified joined differences so that endpoints are recalculated on eve…
puneetkhanduri May 10, 2016
7ba59e2
diffy: modified aggreagted stats to defs from lazy vals to fix stale …
puneetkhanduri May 10, 2016
f350945
Fixing issue with restshifter
May 12, 2016
918c4c0
updating docker-compose to 1.6.0++
May 12, 2016
f43d446
Fixing the mistyped word in the README.md of example
Aug 3, 2016
2224d96
Add mapping of the admin port for diffy container
Aug 3, 2016
ca35bd9
bumped up from openjdk7 to openjdk8
puneetkhanduri Aug 5, 2016
8b6aacc
removed openjdk from travis
puneetkhanduri Aug 5, 2016
070cae2
removed : internal diffy documentation
puneetkhanduri Mar 14, 2017
f976dad
added body extraction for post requests
puneetkhanduri Apr 7, 2017
7b10b20
Add support to HTTPS APIs
gb Mar 24, 2016
b58c8ba
Update README
gb Mar 24, 2016
5d19446
Update travis file, trying to fix the build
gb Mar 25, 2016
b4ecc7c
Update README.md
gb Mar 24, 2016
93cb939
Update protocol description (include https)
gb Mar 28, 2016
71055cf
minor - update protocol description
gb Mar 28, 2016
807bd30
minor - code format / code review suggestions
gb May 23, 2016
42b0d86
Added simple example and updated README instructions
puneetkhanduri Nov 13, 2017
eea03db
Update README.md
puneetkhanduri Jan 7, 2018
787bfc2
Update README.md
puneetkhanduri Jan 7, 2018
b348351
Update README.md
puneetkhanduri Jan 8, 2018
af1cb5f
Exclude HTTP headers in Diffy comparison by default
mgifos Mar 7, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ lib_managed/
.ivyjars
out/
sbt-launch.jar
example/*.class
13 changes: 6 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
sudo: true
language: scala

scala:
- 2.10.5
- 2.11.7

jdk:
- openjdk7
- oraclejdk7
- oraclejdk8

cache:
directories:
- $HOME/.ivy2/cache
- $HOME/.sbt/boot
- $HOME/.sbt/boot/scala-$TRAVIS_SCALA_VERSION

script:
- unset SBT_OPTS
- ./sbt ++$TRAVIS_SCALA_VERSION clean coverage test coverageReport
before_script: unset SBT_OPTS

script: sbt clean coverage test coverageReport

after_script:
- find $HOME/.sbt -name "*.lock" | xargs rm
- find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm

after_success: bash <(curl -s https://codecov.io/bash)

41 changes: 24 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Diffy

[![GitHub license](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
[![Build status](https://img.shields.io/travis/twitter/diffy/master.svg)](https://travis-ci.org/twitter/diffy)
[![Coverage status](https://img.shields.io/codecov/c/github/twitter/diffy/master.svg)](https://codecov.io/github/twitter/diffy)
[![Project status](https://img.shields.io/badge/status-active-brightgreen.svg)](#status)
Expand All @@ -19,6 +20,7 @@ the running instances. It then compares the responses, and reports any regressio
from those comparisons. The premise for Diffy is that if two implementations of the service return
“similar” responses for a sufficiently large and diverse set of requests, then the two
implementations can be treated as equivalent and the newer implementation is regression-free.
For a more detailed analysis of Diffy checkout this [blogpost](https://blog.twitter.com/engineering/en_us/a/2015/diffy-testing-services-without-writing-tests.html).

## How does Diffy work?

Expand All @@ -37,28 +39,29 @@ things:
2. Non-deterministic noise observed between the primary and secondary instances. Since both of these
instances are running known-good code, you should expect responses to be in agreement. If not,
your service may have non-deterministic behavior, which is to be expected.
![Diffy Topology](https://g.twimg.com/blog/blog/image/Diffy_2.png)

Diffy measures how often primary and secondary disagree with each other vs. how often primary and
candidate disagree with each other. If these measurements are roughly the same, then Diffy
determines that there is nothing wrong and that the error can be ignored.

## How to get started?
# Running the example
The example.sh script included here builds and launches example servers as well as a diffy instance. Verify
that the following ports are available (9000, 9100, 9200, 8880, 8881, & 8888) and run `./example/run.sh start`.

First, you need to build Diffy by invoking `./sbt assembly` from your diffy directory. This will create
a diffy jar at `diffy/target/scala-2.11/diffy-server.jar`.

Diffy comes bundled with an example.sh script that you can run to start comparing examples instances
we have already deployed online. Once your local Diffy instance is deployed, you send it a few requests
via `curl --header "Canonical-Resource: Html" localhost:8880` and `curl --header "Canonical-Resource: Json" localhost:8880/json`. You can then go to your browser at
Once your local Diffy instance is deployed, you send it a few requests
like `curl --header "Canonical-Resource: Json" localhost:8880/json?Twitter`. You can then go to your browser at
[http://localhost:8888](http://localhost:8888) to see what the differences across our example instances look like.

# Digging deeper
That was cool but now you want to compare old and new versions of your own service. Here’s how you can
start using Diffy to compare three instances of your service:

1. Deploy your old code to `localhost:9990`. This is your primary.
2. Deploy your old code to `localhost:9991`. This is your secondary.
3. Deploy your new code to `localhost:9992`. This is your candidate.
4. Download the latest Diffy binary or build your own from the code.
4. Download the latest Diffy binary from maven central or build your own from the code using `./sbt assembly`.
5. Run the Diffy jar with following command line arguments:

```
Expand All @@ -68,27 +71,31 @@ start using Diffy to compare three instances of your service:
-master.secondary=localhost:9991 \
-service.protocol=http \
-serviceName=My-Service \
-proxy.port=:31900 \
-admin.port=:31159 \
-http.port=:31149 \
-rootUrl='localhost:31149'
-proxy.port=:8880 \
-admin.port=:8881 \
-http.port=:8888 \
-rootUrl='localhost:8888'
```

6. Send a few test requests to your Diffy instance on its proxy port:

```
curl localhost:31900/your/application/route?with=queryparams
curl localhost:8880/your/application/route?with=queryparams
```

7. Watch the differences show up in your browser at [http://localhost:31149](http://localhost:31149).

### Running example on docker-compose
7. Watch the differences show up in your browser at [http://localhost:8888](http://localhost:8888).

Inside the example directory you will find instructions to run a complete example with apis and diffy configured and ready to run using docker-compose.

## FAQ's
For safety reasons `POST`, `PUT`, ` DELETE ` are ignored by default . Add ` -allowHttpSideEffects=true ` to your command line arguments to enable these verbs.

## HTTPS
If you are trying to run Diffy over a HTTPS API, the config required is:

-service.protocol=https

And in case of the HTTPS port be different than 443:

-https.port=123

## License

Expand Down
11 changes: 0 additions & 11 deletions example.sh

This file was deleted.

71 changes: 71 additions & 0 deletions example/ExampleServers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import java.net.InetSocketAddress;
import java.io.IOException;
import java.io.OutputStream;
import java.util.function.Function;
import java.util.stream.Stream;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class ExampleServers {
public static void main(String[] args) throws Exception {
int primary = Integer.parseInt(args[0]);
int secondary = Integer.parseInt(args[1]);
int candidate = Integer.parseInt(args[2]);
Thread p = new Thread(() -> bind(primary, x -> x.toLowerCase()));
Thread s = new Thread(() -> bind(secondary, x -> x.toLowerCase()));
Thread c = new Thread(() -> bind(candidate, x -> x.toUpperCase()));
p.start();
s.start();
c.start();
while(true){
Thread.sleep(10);
}
}

public static void bind(int port, Function<String, String> lambda) {
try {
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
server.createContext(
"/json",
new Handler(
"{\"name\":\"%s\", \"timestamp\":\"%s\"}",
"application/json",
lambda));
server.createContext(
"/html",
new Handler(
"<body><name>%s</name><timestamp>%s</timestamp></body>",
"text/html",
lambda));
server.setExecutor(null);
server.start();
} catch (Exception exception) {
System.err.println("!!!failed to start!!!");
}
}
}
class Handler implements HttpHandler {
private String template;
private String contentType;
private Function<String, String> lambda;
public Handler(String template, String contentType, Function<String, String> lambda) {
super();
this.template = template;
this.contentType = contentType;
this.lambda = lambda;
}

@Override
public void handle(HttpExchange t) throws IOException {
String name = lambda.apply(t.getRequestURI().getQuery());
String response = String.format(template, name, System.currentTimeMillis());
System.out.println(response);
t.getResponseHeaders().add("Content-Type", contentType);
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
60 changes: 0 additions & 60 deletions example/README.md

This file was deleted.

34 changes: 0 additions & 34 deletions example/docker-compose.yml

This file was deleted.

10 changes: 0 additions & 10 deletions example/flavors/candidate/endpointcandidate.flavor

This file was deleted.

10 changes: 0 additions & 10 deletions example/flavors/candidate/endpointfoocandidate.flavor

This file was deleted.

10 changes: 0 additions & 10 deletions example/flavors/candidate/endpointmehcandidate.flavor

This file was deleted.

10 changes: 0 additions & 10 deletions example/flavors/primary/endpointfooprimary.flavor

This file was deleted.

10 changes: 0 additions & 10 deletions example/flavors/primary/endpointmehprimary.flavor

This file was deleted.

10 changes: 0 additions & 10 deletions example/flavors/primary/endpointprimary.flavor

This file was deleted.

10 changes: 0 additions & 10 deletions example/flavors/secondary/endpointfoosecondary.flavor

This file was deleted.

10 changes: 0 additions & 10 deletions example/flavors/secondary/endpointmehsecondary.flavor

This file was deleted.

Loading