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

dump: Rework command to encode #318

Merged
merged 11 commits into from
Aug 18, 2018
Merged

dump: Rework command to encode #318

merged 11 commits into from
Aug 18, 2018

Conversation

xla
Copy link
Contributor

@xla xla commented Jul 14, 2018

In order to give the command more utility and play well with pipeline idea of the vegeta toolchain we decided per discussion[0] to change the semantics of dump. We offer now a general encode command which can transform any supported input encoding to any supported output encoding. We anticipate that in conjunction with advocated auxiliary tools, like jq, a wide range of use-cases can be covered without extending vegeta itself.

[0] #302 (comment)

Closes #302

Background

Checklist

  • Git commit messages conform to community standards.
  • Each Git commit represents meaningful milestones or atomic units of work.
    - [ ] Changed or added code is covered by appropriate tests.

@xla xla self-assigned this Jul 14, 2018
@xla xla requested a review from tsenart July 14, 2018 11:42
encode.go Outdated
fs.PrintDefaults()
}

return command{

Choose a reason for hiding this comment

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

github.com/tsenart/vegeta.command composite literal uses unkeyed fields

Copy link
Owner

Choose a reason for hiding this comment

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

Ugh, OK, let's comply. Other places are in transgression too.

encode.go Outdated
return command{
fs,
func(args []string) error {
fs.Parse(args)

Choose a reason for hiding this comment

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

Error return value of fs.Parse is not checked

Copy link
Owner

Choose a reason for hiding this comment

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

Since we introduced @golangci, let's make it happy: _ = fs.Parse()

Dumper [json, csv] (default "json")
-inputs string
Input files (comma separated) (default "stdin")
encode command:
Copy link
Owner

Choose a reason for hiding this comment

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

This listing of command's flags in vegeta -h was alright when commands only took flags but no positional arguments. Since this is changing, I believe we need to re-evaluate.

Either we display the full help out put of each sub command here, or we only display a list of sub command names, and users will have to defer to vegeta subcmd -h to read more.

What's your take?

Copy link
Owner

Choose a reason for hiding this comment

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

I will implement this in a follow up PR.

README.md Outdated
#### `-output`
Specifies the output file to which the dump will be written to.
#### `-from`
Specifies the decoding format of the inputs.
Copy link
Owner

Choose a reason for hiding this comment

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

I think it'd be more correct to say "the encoding format of the inputs".

README.md Outdated
encoding](https://golang.org/pkg/encoding/gob).

##### `json`
Decodes/encodes results as JSON objects.
Copy link
Owner

Choose a reason for hiding this comment

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

To be correct, it's JSONLines: http://jsonlines.org/

encode.go Outdated

func encodeCmd() command {
var (
fs = flag.NewFlagSet("encode", flag.ExitOnError)
Copy link
Owner

Choose a reason for hiding this comment

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

This should be flag.NewFlagSet("vegeta encode", ...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why? vegeta is already the main namespace of the binary.

Copy link
Owner

Choose a reason for hiding this comment

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

Consistency with the other sub commands. Also, the different flag sets aren't directly associated in any way. This command could potentially work by itself if the parent didn't exist.

encode.go Outdated
fs = flag.NewFlagSet("encode", flag.ExitOnError)
from = fs.String("from", "gob", "Input decoding [csv, gob, json]")
to = fs.String("to", "json", "Output encoding [csv, gob, json]")
output = fs.String("output", "", "Output file")
Copy link
Owner

Choose a reason for hiding this comment

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

The default output should be stdout

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This doesn't add up really. We expect output to be a filename, if we expect stdout as ivalue it is inconsistent. They don't have the same meaning or refer to the same thing. Arguably we shoul document the default case better.

Copy link
Owner

Choose a reason for hiding this comment

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

We expect output to be a filename.

Sure, and if you so wished you should be able to pass /dev/stdout to it and it'd just work. stdout is in this case a symbolic name that resolves to that path in practice.

Copy link
Owner

Choose a reason for hiding this comment

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

Plus, it documents the behaviour, from the user perspective, it makes it explicit in the output of vegeta encode -h that the output will be to stdout, instead of that being implicit.

encode.go Outdated
decFn = vegeta.NewCSVDecoder
case encodingJSON:
decFn = vegeta.NewJSONDecoder
default:
Copy link
Owner

Choose a reason for hiding this comment

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

Do we need to rely on the default case to play nice with the pipes as per your comment? The default "gob" could come from the flags outside this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, let's error in if we don't know the given encoding.

encode.go Outdated

decs := []vegeta.Decoder{}

if len(inputs) > 0 {
Copy link
Owner

Choose a reason for hiding this comment

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

We have some simpler code to handle this already. i.e. https://github.com/tsenart/vegeta/blob/master/report.go#L33

Because the file function returns os.Stdin for the string stdin. This block of code can become:

	srcs := make([]vegeta.Decoder, len(files))
	for i, f := range inputs {
		in, err := file(f, false)
		if err != nil {
			return err
		}
		defer in.Close()
		srcs[i] = vegeta.NewDecoder(in)
	}
	dec := vegeta.NewRoundRobinDecoder(srcs...)

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 find the file function an anti-pattern and don't really understand the gains. os.Create and os.Open are so much more expresisve, a fact hidden behind an opaque boolean. Having string literals to refer to the the input and output file handles is unnecesssary indirection imho.

Copy link
Owner

Choose a reason for hiding this comment

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

Yeah, file is slightly conflated. We can take out the file creation part from it and use os.Create directly where needed, thus scoping the symbolic resolution to opening files only, not creating (since you don't create stdout or stdin).

func open(file string) (*os.File, error) {
    switch file {
    case "stdout":
        return os.Stdout, nil
    case "stdin":
        return os.Stdin, nil
    default:
        return os.Open(file)
}

encode.go Outdated
decs = append(decs, decFn(os.Stdin))
}

in := vegeta.NewRoundRobinDecoder(decs...)
Copy link
Owner

Choose a reason for hiding this comment

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

I am wondering what makes most sense to do. Shall we read results in a round-robin fashion and re-encode each out as they come or should we instead read all of them, sort them and then write them out. Perhaps this should be a flag.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is the upside of the sorting? And sorting per input, over all inputs, by what criteria?

Copy link
Owner

Choose a reason for hiding this comment

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

I think we can delegate this to external tools, indeed.

cat results.bin | vegeta encode | jq -s 'sort_by(.timestamp)'

encode.go Outdated

var out vegeta.Encoder

if output != "" {
Copy link
Owner

Choose a reason for hiding this comment

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

output should never be empty. The default stdout ought to be set in the flags. Then you can reuse the file function as in the report command.

Copy link
Owner

Choose a reason for hiding this comment

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

Additionally, if you open the output first and then validate the to value, you don't need encoderFunc.

var enc vegeta.Encoder
switch to {
case encodingCSV:
    enc = vegeta.NewCSVEncoder(out)
case encodingJSON:
    enc = vegeta.NewJSONEncoder(out)
case encodingGob:
    enc = vegeta.NewEncoder(out)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To your first comment see #318 (comment)

To the second comment I usually try to check all parameters before acquiring system resources (fds, conns, etc.).

Copy link
Owner

Choose a reason for hiding this comment

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

To the second comment I usually try to check all parameters before acquiring system resources (fds, conns, etc.).

What does it gain us here?

encode.go Outdated
out = encFn(os.Stdout)
}

for {
Copy link
Owner

Choose a reason for hiding this comment

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

We need to handle signals gracefully as in the report command.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will extend!

encode.go Outdated
}

return command{fs, func(args []string) error {
fs.Parse(args)

Choose a reason for hiding this comment

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

Error return value of fs.Parse is not checked

xla and others added 6 commits August 18, 2018 18:28
In order to give the command more utility and play well with pipeline
idea of the vegeta toolchain we decided per discussion[0] to change the
semantics of dump. We offer now a general encode command which can
transofrm any input encoding to any of the supported output encodings.
We anticipate that in conjunction with advocated auxiliary tools, like\
jq, a wide range of use-cases can be covered without extending vegeta
itself.

[0] https://github.com/tsenart/vegeta/issues/302#issuecomment-403935450<Paste>

Closes #302
}

return command{fs, func(args []string) error {
fs.Parse(args)

Choose a reason for hiding this comment

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

Error return value of fs.Parse is not checked

@tsenart tsenart merged commit c9bc7f7 into master Aug 18, 2018
@tsenart tsenart deleted the 302-dump-to-encode branch August 18, 2018 20:32
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