Skip to content

Commit

Permalink
update documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
eze-kiel committed Feb 24, 2021
1 parent b53931c commit 73d6df4
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 5 deletions.
74 changes: 72 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,76 @@ A slow query logs parser in Golang.
go get github.com/devops-works/slowql
```

## Usage
## Basic usage

You have a working example [in cmd/](https://github.com/devops-works/slowql/blob/main/cmd/main.go).
You have a working example [in cmd/](https://github.com/devops-works/slowql/blob/main/cmd/main.go).

To put in a nutshell, you can use it as follows:

```go
package main

import (
"fmt"

"github.com/devops-works/slowql"
)

func main() {
// Imagine that fd is an io.Reader of your slow query logs file...

// Create the parser
p := slowql.NewParser(fd)

// Get the next query from the log
q, err := p.GetNext()
if err != nil {
panic(err)
}

// Do your stuff, for example:
fmt.Printf("at %d, %s did the request: %s\n", q.Time, q.User, q.Query)

fp, err := q.Fingerprint()
if err != nil {
panic(err)
}
fmt.Printf("the query fingerprint is: %s\n", fp)
}
```

## Performances

Running the example given in cmd/ without any `fmt.Printf` against a 292MB slow query logs from a MySQL database provides the following output:

```
parsed 278077 queries in 8.099622786s
```

which is approx. **34760 blocs/seconds** (a bloc being composed of the headers and the request itself).

## Notes

### Tested databases

Not all kind of slow query logs have been tested yet:

- [X] MySQL
- [X] MariaDB
- [ ] Percona-db
- [ ] Percona-cluster (pxc)

### Internal

In the background, when you call `slowql.NewParser`, it starts two goroutines. The first one will read the file line by line and assemble them to create "blocs" composed of hearders (starting with #) and requests. Once created, those blocs are send through a channel.
The second goroutine will intercept blocs and populate a `Query` struct with the values that matches its rules. Once the bloc parsed, the `Query` object is sent through another channel which is bufferized. Actually, it has a buffer size of `parser.StackSize`. When you call `parser.GetNext()`, you simply get the first value of the channel's buffer.

The goroutines only stop when there is nothing more to read from the log file. So once the buffer is full, only a little part of CPU workload will be used to keep the buffer full each time a value is extracted.

## Contributing

Issues and pull request are welcomed ! If you found a bug or want to help and improve this package don't hesitate to fork it or open an issue :smile:

## License

MIT
6 changes: 3 additions & 3 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ func NewParser(r io.Reader) *Parser {
return &p
}

// GetNext returns the next query. Some fields of Query may be empty, depending
// on what has been parsed from the log file.
// GetNext returns the next query, starting from the bottom. Some fields of Query
// may be empty, depending on what has been parsed from the log file.
func (p Parser) GetNext() (Query, error) {
var q Query
select {
case q := <-p.stack:
return q, nil
case <-time.After(time.Second * 10):
case <-time.After(time.Second * 2):
close(p.stack)
}
return q, nil
Expand Down

0 comments on commit 73d6df4

Please sign in to comment.