-
Notifications
You must be signed in to change notification settings - Fork 13
Two identical parse requests can return different results #54
Comments
Out of curiosity, have you seen this happen with languages other than JS? I'm asking since it's not yet clear whether this points to a driver bug (which I suspect) or an SDK bug (which is also possible). |
No, but it is fast to check, so I did it. Thanks for the question because I found a bug in my code snippet and fix it (first msg updated). But the bug is still on place. So, I run the test for latest python driver and https://github.com/src-d/style-analyzer repo and it looks fine. The bblfsh output was the same during 28 iterantions and then I stop it. I think we can safely move this one to https://github.com/bblfsh/javascript-driver/issues |
I agree, though I don't seem to have the permissions to do that. |
@smola can I kindly ask you to move this issue to https://github.com/bblfsh/javascript-driver? |
Was trying to reproduce this with latest Golang v3 client instead of old python v1, to check if this is fixed in latest version using the code from details
and it is not reproduced exactly, but out of 112, there was only a single file First run
Another run
Other then that, there is no difference on 10 iterations using latest UASTv2-enabled client. package main
//git clone https://github.com/laravel/telescope; cd telescope
//git checkout 6f0a10ec586cfa1a22218b6778bf9c1572b97912; cd ..
//go run bblfsh_js.go
import (
"fmt"
"os"
"path/filepath"
"github.com/sanity-io/litter"
"gopkg.in/bblfsh/client-go.v3"
)
const (
numberOfIterations = 2
dataDir = "telescope"
)
var filesStatuses map[string]map[string]int
func main() {
litter.Config.Compact = true
filesStatuses = make(map[string]map[string]int)
client, err := bblfsh.NewClient("127.0.0.1:9432")
if err != nil {
panic(err)
}
for i := 0; i < numberOfIterations; i++ {
processed := parseEveryFileIn(dataDir, client)
fmt.Printf("Iter: %d done, processed: %d\n", i, processed)
}
fmt.Printf("\nTotal files: %d\n", len(filesStatuses))
for file, statuses := range filesStatuses {
for _, count := range statuses {
if count != numberOfIterations {
fmt.Printf("\t file: %s, %d statuses: %v\n", file, len(statuses), litter.Sdump(statuses))
}
break
}
}
}
func parseEveryFileIn(dir string, client *bblfsh.Client) int {
processed := 0
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("failed to access the path %q: %v\n", path, err)
return err
}
if info.IsDir() && info.Name() == ".git" { //do not walk .git/*
return filepath.SkipDir
}
if info.IsDir() { //do not process any dir
return nil
}
_, lang, err := client.NewParseRequest().ReadFile(path).UAST()
status := status(lang, err)
if _, ok := filesStatuses[path]; ok {
fs := filesStatuses[path]
if _, ok := fs[status]; !ok {
fmt.Printf("\tNew status for %s: '%s'\n", path, status)
}
fs[status]++
} else {
//fmt.Printf("\tProcessing '%s' got '%s'\n", path, status)
filesStatuses[path] = make(map[string]int)
filesStatuses[path][status] = 1
}
processed++
return nil
})
if err != nil {
fmt.Printf("error walking the path %q: %v\n", dir, err)
}
return processed
}
func status(lang string, err error) string {
return fmt.Sprintf("lang:'%s' err:'%s'", lang, err)
} Next step would be to confirm if this behavior of the latest client version is consistent with new Python client bblfsh/python-client#128 BTW, the offender here is this 2.4mb app.js file and same behavior (randomized errors) is easily reproducible with multiple subsequent runs of |
@bzz This helps a lot, actually. The output of a failing transformation is non-deterministic (random map iteration). But it is deterministic for a successful one. Can you please open an issue with the file that generates those errors? Thanks! |
I suspect a map-traversal order issue on the error path. |
@creachadair Exactly. Although I'm not sure if we want to change it - it will affect performance. |
I have updated the #54 (comment) above with the link - do you mean we need a separate issue in SDK as well?
Am planing to submit a workaround by sorting them first. |
@bzz It's JS-related issue since it fails to transform a valid JS AST. Regarding sorting errors, I'm not against it, but it won't fix the actual issue with the non-deterministic AST output. This is because AST traversal order is non-deterministic in DSL, and it may terminate the transform on fatal errors (read: randomly depending on the AST branch taken). The random order is intentional since it provides better performance (no allocation or sorting) and provides some level of protection against invalid transforms that rely on iteration order. And it doesn't affect the successful transformation passes. |
makes sense, thank you for explaining! And TBH I do not think it's an issue at all - as soon parsing consistently fails for the same file, it's not a big deal having a slightly different error messages. Ok, will just investigate further why it fails for this particular file, if it's consistent between clients and skip the sorting. And examples of code in here might be useful for future regression testing automation that we can run on every release. |
I don't think performance on an error path is a concern: When we get an error, we're already committed to aborting a fairly expensive transaction, so it won't happen in a loop. In contrast, having to do subtle workarounds to compensate for time-varying output in the "bad" cases is almost certainly not worth it. Reproducibility is really really helpful. :) |
Completely agree with @creachadair |
I agree with a general concern about reproducibility. And the successful transformation have this already - in other cases all fixture tests fill fail randomly, which doesn't happen. @creachadair The problem is that the issue @bzz found is not on an error path. The main code path does this random iteration for performance reasons. Sorting keys for each node on every transform step and for every suboperation will have a very severe performance impact. The real question is: why it returns different statuses? Are there any cases when it parses the file for the first time and fails the second time? If so - it's a bug in DSL or SDK. Unrelated to iteration order. |
I agree it doesn't make sense to sort each time. What I guess I'm not clear about is where order matters: If order is only observed at the point where a result is reported to the caller, then it should be fine to sort the results once, at the point of return (e.g., a gRPC handler maybe). If order matters at other times, then we should consider using a different data structure. But it sounds like the real issue is just that the final return value is variable, is that correct? |
It's still not clear to me if the bug that @bzz found is the same as what @zurk originally reported. Because if it's the same, it means that the DSL terminates the transform half-way because of an error and then reports different results because of the random iteration order (different branches visited before an error condition). But if that's not the same, it means that some annotation is flaky and should be fixed. |
I will get back to this as soon as driver is migrated to latest SDK and missing annotations are added (#57) and post the updated results. |
@bzz We are going to port to 2.7.0 (the current 2.6.0 has some problems) in 🔥src-d/style-analyzer#666 😈 and then we will try to reproduce again. Hopefully, it will be healed automagically with the help of Satan. 🔥 |
Awesome! This is still very useful and is open due to #62 right now so yeah 😈 |
v2.7.0 is out now! So far, this issue is not reproducible any more with go-client app from #54 (comment):
@zurk could you verify that on your side please? |
Yes, I can not reproduce the bug. Looks good, thank you! |
Guys, I found one really weird big in bblfsh. I think it is related to daemon itself but cannot be 100% sure. So do not hesitate to move this issue if I am wrong.
Context
Machine learning team really want to have reproducible results everywhere where it is possible.
So we fix all random seeds to have a deterministic code. Suddenly we find out that two identical runs of style-analyzer quality estimation give different results: src-d/style-analyzer#557. Clues lead to bblfsh and I was able to reproduce the bug without our code. Let me show you.
How to reproduce
Run in bash
Also checked for v1.2.0 driver version.
Run in python3
I do not think it is important but bblfsh package version is
2.12.6
.My output head
How to interpret
Mainly, if you see the message, that some file gets a
new status
that means that during the identical requests bblfsh return different statuses. For example, fortelescope/webpack.mix.js
andtelescope/resources/js/routes.js
it was able to parse file first but not the second time.Notes
iter_n
to bigger number you get much more output messages.iter_n
become super bad. Can be related to perf: bblfshd 2.9 non responsive after parsing 1000+ files bblfshd#226iter_n=100
.So it is important for ML team to have reproducible output from bblfsh.
Please tell me if you are able to reproduce the issue or need more info from my side.
The text was updated successfully, but these errors were encountered: