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

Improvements after testing on 2G/3G/4G networks #33

Merged
merged 24 commits into from
Nov 9, 2018
Merged

Conversation

bassosimone
Copy link
Contributor

@bassosimone bassosimone commented Oct 17, 2018

I've run some more tests in 2G/3G/4G networks. This led me to the following improvements:

  1. using a worker for the web client, so the UI is not locked

  2. modify the protocol to allow for sending measurement messages with padding, so that the client receives more measurements regardless of the delay caused by 2G/3G/4G buffers

  3. other, smaller, miscellaneous, useful fixes

I have also experimented with TCP_NOTSENT_LOWAT but that diff is not ready to be included in this PR yet, and I feel like the code in here has been delayed for too much time already. I'll probably ask about this option to @mattmathis later today or in the next days.


This change is Reviewable

Support for metadata was added as part of #31.

Bump client minor because we changed the settings structure.
I would like to see logs similar to the logs typically printed by
Apache for access.log as far as the HTTPS port is concerned.

For other ports, I would argue it does not matter much because
they are basically non standard (others may disagree).
Allows us to update the UI while the testing is running. The previous
code blocked the user interface until the test was complete.

While there, remove now unused ndt7 options: adaptive and duration.
As we've just discussed with other NDT developers at M-Lab, better to
run for ten seconds at this stage to learn how to best stop early.
We need such ID inside the table body. Using it also for the
whole table is incorrect, even if it works.
Allows to unify the code for dealing with missing BBR support
on Linux with the code for all other systems.

Means we can get rid of the concept of "tolerate weird failures
when trying to enable BBR because we MAY be on Linux".

Really, just simplifies code.
Testing in several 2G/3G/4G scenarios, I have noticed that just one
second of timeout is not enough. Better to raise it.

I have picked seven because it is a number I like. It could also have
been four. I'd say we can adjust this further when there's more data
from more clients from diverse networks.
The client.go file will eventually become a regress test, so
server.go is a better place to put that constant in.
[I recommend to use `git show -w` to inspect this diff.]

This commit changes the specification to provide the server the
choice of using either binary or textual messages to generate
network load. The rationale of using binary messages is that it
is more efficient. The rationale of using textual messages is
that every message received by the client is a meaningful piece
of measurements that can be used to perform inferences.

Since measurement messages are too small to generate network
load, I've modified the specification to add random padding to
JSON messages. In this way, messages are large enough.

I have also modified the server to use textual messages and
padding. While in general I believe one MAY want to have a
server sending binary messages, after several tests in {3,4,5}G
networks, I've come to the conclusion that:

- either we further complicate the server implementation with
  more than a single goroutine for the sender, such that we
  collect (and store) measurements every N milliseconds

- or we use padding an every message is meaningful

The specific issue is that, with a slow 2G/3G network, the
sender is blocked in sending the binary messages and does
not have many chances of sampling BBR variables before the
client closes the connection because it's running for too
long (consider that a 10 second test runs for around two
minutes on my 2G network).

Therefore, one possible choice would have been to have a
goroutine for sampling BBR variables. But, still, even with
this sampling, the client would not receive many updates
and much information.

On the contrary, if each send() carries also a measurement,
the user experience is much more pleasant because one at
least sees a stream of measurements, albeit delayed by the
buffering queue in the operator's 2G machinery.

(IIRC @pboothe proposed this strategy to me when we first
discussed ndt7. I initially choose to go for binary messages
because it looked like more efficient, however, putting
measurements and data together turned out to be a bit smarter,
because there is no head-of-line-blocking problem.)
This seems to provide a more pleasant UX.
@coveralls
Copy link
Collaborator

coveralls commented Oct 17, 2018

Pull Request Test Coverage Report for Build 245

  • 0 of 7 (0.0%) changed or added relevant lines in 1 file are covered.
  • 1 unchanged line in 1 file lost coverage.
  • Overall coverage decreased (-0.1%) to 65.532%

Changes Missing Coverage Covered Lines Changed/Added Lines %
ndt-server.go 0 7 0.0%
Files with Coverage Reduction New Missed Lines %
ndt-server.go 1 65.53%
Totals Coverage Status
Change from base Build 189: -0.1%
Covered Lines: 308
Relevant Lines: 470

💛 - Coveralls

While that was already clear in the documentation, it was not
explicit in variable names and in JSON message fields.

Yesterday, talking to @mattmathis, I made the mistake of
considering the min-RTT being the RTT, which led me to draw
wrong conclusions regarding a connection.

For this reason, I have decided to go a bit more explicit
with variable names. If the person who wrote the code, i.e.
me, wrongly interprets variables, this may happen as well
to someone less familiar with the codebase. Hence, it is
clearly better to have a much less ambiguous naming.

No need to bump the specification version because it has
already been bumped as part of this PR.
Since version 0.3.0 of the spec, there is no min-measurement-interval
as it's possible to use just measurement messages to load the net.

Hence, just move the variable inside the client.go file.

Sometime in the future, client.go will be moved in another place
and become a unit test for the server.
Copy link
Contributor

@pboothe pboothe left a comment

Choose a reason for hiding this comment

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

I am sorry this took me so long to get to.

It looks good to me, so :lgtm: , but I have one easy fix requested: please switch to using URL and URLSearchParams instead of hand-coding our own "safe URL" logic. URLs are secretly a little tricky to get precisely right, so using standard libraries helps provide assurance.

Reviewed 2 of 10 files at r1, 4 of 9 files at r2.
Reviewable status: :shipit: complete! 1 of 1 LGTMs obtained


html/libndt7.js, line 62 at r2 (raw file):

        }
        url += '/ndt/v7/download?'
        for (let key in settings.meta) {

I recommend the URL and URLSearchParams classes here. They will ensure that you don't make malformed URLs and that you don't have to think about whether your URL is valid, and they will URLEncode any non-ASCII (but legitimate) values like the Chinese name of the Chinese version of Windows.

@bassosimone
Copy link
Contributor Author

@pboothe, thanks for your review! I have been seriously swamped lately because the upcoming end of my fellowship, so I could not add the requested changes yet. -EAGAIN. Will retry tomorrow! :-)

1. use `URL` and `URLSearchParams`

2. notice that we can simplify further and just pass around
   `window.location.href` as the base URL

3. disable a `console.log` statement that, with an open
   console, was tearing down my browser

See: #33 (review)
@bassosimone
Copy link
Contributor Author

@pboothe I have implemented you suggestions, please take a look!

@pboothe
Copy link
Contributor

pboothe commented Nov 2, 2018

Then changes look good, although perhaps things can be even 4 lines simpler in the JS? However you decide to go, this change looks good to me.

html/libndt7.js Outdated
if (typeof value === 'number' || typeof value === 'boolean') {
value += '' // force conversion to string
}
if (typeof value !== 'string' || !value.match(re)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should be okay with unicode values and that it is okay to remove the value.match(re) check now that we can be sure that the URLencoding will be okay in all circumstances.

Copy link
Contributor

Choose a reason for hiding this comment

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

Whether or not unicode keys make sense, I leave to you. But these checks feel redundant to me.

Copy link
Contributor Author

@bassosimone bassosimone Nov 6, 2018

Choose a reason for hiding this comment

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

So, my understanding is that strings in JavaScript are UTF-16. Reading the polyfill code for URLSearchParams, I additionally understand that it should call encodeURIComponent. In turn, that should serialise to UTF-8.

Is my understanding correct?

I am concerned that JavaScript strings are UTF-16 while Go strings are UTF-8. So, I would like to be sure that removing the checks enforcing ASCII we do not end up with URL params that we cannot use in Go.

(This is probably just me being paranoid and not knowing enough about the Web.)

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 a clarification, I am not thinking about using such params to build file names. But my code in another branch is serialising such params to JSON as part of saving results. Hence my concern before applying the objective improvement that you suggest. (As a note to self, if I do that, I shall also update the spec.)

Copy link
Contributor

Choose a reason for hiding this comment

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

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/url"
	"strings"
)

func main() {
	m, err := url.ParseQuery(`x=1&y=2&w=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B&y=3;z`)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(toJSON(m))
}

func toJSON(m interface{}) string {
	js, err := json.Marshal(m)
	if err != nil {
		log.Fatal(err)
	}
	return strings.Replace(string(js), ",", ", ", -1)
}

results in:

{"w":["шеллы"], "x":["1"], "y":["2", "3"], "z":[""]}

So it looks like UTF16 bytes are handled by Go just fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Excellent, thanks! So, I'll remove these four lines and merge :-)

@bassosimone bassosimone merged commit b9cee1a into master Nov 9, 2018
@bassosimone bassosimone deleted the sandbox-sbs branch November 9, 2018 11:47
satomiie pushed a commit to satomiie/performance-ndt-server that referenced this pull request Apr 16, 2020
1. use `URL` and `URLSearchParams`

2. notice that we can simplify further and just pass around
   `window.location.href` as the base URL

3. disable a `console.log` statement that, with an open
   console, was tearing down my browser

See: m-lab/ndt-server#33 (review)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants