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

Unable to resolve expression during build #1306

Closed
bodiam opened this issue Jul 15, 2024 · 12 comments · Fixed by #1311 or #1381
Closed

Unable to resolve expression during build #1306

bodiam opened this issue Jul 15, 2024 · 12 comments · Fixed by #1311 or #1381
Assignees
Labels
bug Something isn't working

Comments

@bodiam
Copy link
Contributor

bodiam commented Jul 15, 2024

Hi all,

I have another build where an expression couldn't be resolved:
https://github.com/datafaker-net/datafaker/actions/runs/9940728395/job/27458128814?pr=1305

I was hoping it was resolved in one of the earlier 2.3.x PRs, but it seems it's still happening, I had a few failures earlier today too.

If someone could have a look, that would be great!

@bodiam bodiam added the help wanted Extra attention is needed label Jul 15, 2024
@asolntsev
Copy link
Collaborator

I don't yet know what's the reason, but at least I know how to reproduce it.
Just run this single test in IDEA, then open "Run Configurations" -> "Edit configurations" -> "Modify options" -> "Repeat" -> "Until failure". And run again.

Now test will be re-running endlessly, and sooner or later will fail with the expected error.

image
image

@snuyanzin
Copy link
Collaborator

@RVRhub I remember you also mentioned some findings about this

@asolntsev
Copy link
Collaborator

@bodiam @snuyanzin I could reproduce the failure, but still don't understand why it happens.
What I suggest:
I can submit a PR that improves logging and adds debug logs at some places in DF.
Then we can get more information from debug logs when it happens again.

@kingthorin
Copy link
Collaborator

I'm good with that 👆

Though I'm not exactly an authority for the project 😁

@bodiam
Copy link
Contributor Author

bodiam commented Jul 20, 2024

I can submit a PR that improves logging and adds debug logs at some places in DF.

I don't mind that, but perhaps for narrowing down the problem here only? It's probably not something we need to merge right, the debugging can happen on a branch?

@asolntsev
Copy link
Collaborator

@bodiam It depends on the logging itself. But generally I think it's a good idea to add the logging to main branch.
DEBUG logs are disabled by default, so it will not affect users. But when someone decides to investigate some specific problem, they can easily enable debug logs for the entire DF or some specific package.

@bodiam
Copy link
Contributor Author

bodiam commented Jul 20, 2024

I've done some debugging, and this is what I see:

image

It's coming from here:

        Map<String[], MethodAndCoercedArgs> accessorMap =
            MAP_OF_METHOD_AND_COERCED_ARGS
                .getOrDefault(clazz, Collections.emptyMap())
                .getOrDefault(methodName, Collections.emptyMap());
        // value could be null
        if (accessorMap.containsKey(args)) {  <--- accessorMap is sometimes null
            return accessorMap.get(args);
        }

I don't think the accessorMap can be null under normal circumstances, but this is the COWMap, I'm not sure if that's related, but not sure how accessorMap can be null here (sometimes). When it's null, the nullpointer is swallowed by the above code when it's catching Exception.

@asolntsev
Copy link
Collaborator

Yes, this is one of my findings too. And proper logging would help to find this issue sooner. ;)
But it doesn't seem to cause our initial problem. :(

@bodiam
Copy link
Contributor Author

bodiam commented Jul 20, 2024

@asolntsev See screenshot. There is a log message exactly there: LOG.fine(e.getMessage()). That prints the nullpointer when you enable logging.

But I think it's related to the initial problem, since the above exception happens when some method returns null. In this case, null is returned when we get the NPE. Why do we get the NPE? I have no idea yet.

@asolntsev
Copy link
Collaborator

Why do we get the NPE?

As a rule, exceptions must be logged with stack trace:
Log.log(FINE, e.getMessage(), e);

Then we would know why it happens.
And I don't understand why this log is FINE, not ERROR or at least WARNING.

Anyway, I am working on it.

asolntsev added a commit that referenced this issue Jul 21, 2024
it's a simplified version of what happens with FakeValuesService.MAP_OF_METHOD_AND_COERCED_ARGS
asolntsev added a commit that referenced this issue Jul 21, 2024
it's a simplified version of what happens with FakeValuesService.MAP_OF_METHOD_AND_COERCED_ARGS
@asolntsev
Copy link
Collaborator

asolntsev commented Jul 21, 2024

@bodiam @snuyanzin After some investigation, I came to a conclusion that this is a bug in CopyOnWriteMap, or at least its usage.

I reproduced this problem in a dedicated unit-tests here: #1310

P.S. This is how CopyOnWriteMap is used in FakeValuesService:

  final Map<String, Map<String[], MethodAndCoercedArgs>> stringMapMap =
    MAP_OF_METHOD_AND_COERCED_ARGS.computeIfAbsent(clazz, t -> new CopyOnWriteMap<>(WeakHashMap::new));

  // Step 1: we PUT value to map, but...
  stringMapMap.putIfAbsent(methodName, new CopyOnWriteMap<>(WeakHashMap::new));

  // Step 2: `map.get` returns NULL!
  stringMapMap.get(methodName).putIfAbsent(args, accessor); // here `stringMapMap.get(methodName)` is NULL

Assumably, null is returned after GC run.

@asolntsev asolntsev linked a pull request Jul 21, 2024 that will close this issue
asolntsev added a commit that referenced this issue Jul 21, 2024
…olver)

it allows to log what objects were returned when debugging flaky test failures.
asolntsev added a commit that referenced this issue Jul 21, 2024
... to make it easier to read these values in logs
asolntsev added a commit that referenced this issue Jul 21, 2024
... especially with FINE level (== DEBUG).

This pattern causes flaky failures that are very hard to debug.
If something went wrong, it's always safer to throw it with a clear description of what happened.

See "Throw early, catch late" principle.
asolntsev added a commit that referenced this issue Jul 21, 2024
…fore()

It was WTF moment when I realized that method BaseFakerTest.before() resets all logger levels to INFO.
It took few hours to find out why my logging configuration doesn't work. :(
asolntsev added a commit that referenced this issue Jul 21, 2024
* by default, FINE logs are NOT written to console, BUT
* if some test failed, all its FINE logs are written to console.

Thus we can investigate flaky tests.
asolntsev added a commit that referenced this issue Jul 21, 2024
there is no need to initialize static Faker's vars in some specific way. They should stably work in any order.
asolntsev added a commit that referenced this issue Jul 21, 2024
... especially with FINE level (== DEBUG).

This pattern causes flaky failures that are very hard to debug.
If something went wrong, it's always safer to throw it with a clear description of what happened.

See "Throw early, catch late" principle.
asolntsev added a commit that referenced this issue Jul 21, 2024
…fore()

It was WTF moment when I realized that method BaseFakerTest.before() resets all logger levels to INFO.
It took few hours to find out why my logging configuration doesn't work. :(
asolntsev added a commit that referenced this issue Jul 21, 2024
* by default, FINE logs are NOT written to console, BUT
* if some test failed, all its FINE logs are written to console.

Thus we can investigate flaky tests.
asolntsev added a commit that referenced this issue Jul 21, 2024
there is no need to initialize static Faker's vars in some specific way. They should stably work in any order.
@asolntsev
Copy link
Collaborator

@asolntsev asolntsev added bug Something isn't working and removed help wanted Extra attention is needed labels Jul 21, 2024
@asolntsev asolntsev self-assigned this Jul 21, 2024
asolntsev added a commit that referenced this issue Jul 28, 2024
Instead of two separate calls `putIfAbsent` and `get`, now we use `computeIfAbsent`.
Before this change, `get` sometimes returned null (apparently, because of GC or parallel threads or something similar).
asolntsev added a commit that referenced this issue Jul 28, 2024
…ervice

Instead of three separate calls `containsKey`, `get` and `put`, now we use a single `computeIfAbsent`.
asolntsev added a commit that referenced this issue Jul 28, 2024
this partially reverts commit 43e1fff
asolntsev added a commit that referenced this issue Aug 23, 2024
…olver)

it allows to log what objects were returned when debugging flaky test failures.
asolntsev added a commit that referenced this issue Aug 23, 2024
... to make it easier to read these values in logs
asolntsev added a commit that referenced this issue Aug 23, 2024
... especially with FINE level (== DEBUG).

This pattern causes flaky failures that are very hard to debug.
If something went wrong, it's always safer to throw it with a clear description of what happened.

See "Throw early, catch late" principle.
asolntsev added a commit that referenced this issue Aug 23, 2024
…fore()

It was WTF moment when I realized that method BaseFakerTest.before() resets all logger levels to INFO.
It took few hours to find out why my logging configuration doesn't work. :(
asolntsev added a commit that referenced this issue Aug 23, 2024
* by default, FINE logs are NOT written to console, BUT
* if some test failed, all its FINE logs are written to console.

Thus we can investigate flaky tests.
asolntsev added a commit that referenced this issue Aug 23, 2024
there is no need to initialize static Faker's vars in some specific way. They should stably work in any order.
asolntsev added a commit that referenced this issue Aug 23, 2024
…olver)

it allows to log what objects were returned when debugging flaky test failures.
asolntsev added a commit that referenced this issue Aug 23, 2024
... to make it easier to read these values in logs
asolntsev added a commit that referenced this issue Aug 23, 2024
... especially with FINE level (== DEBUG).

This pattern causes flaky failures that are very hard to debug.
If something went wrong, it's always safer to throw it with a clear description of what happened.

See "Throw early, catch late" principle.
asolntsev added a commit that referenced this issue Aug 23, 2024
…fore()

It was WTF moment when I realized that method BaseFakerTest.before() resets all logger levels to INFO.
It took few hours to find out why my logging configuration doesn't work. :(
asolntsev added a commit that referenced this issue Aug 23, 2024
* by default, FINE logs are NOT written to console, BUT
* if some test failed, all its FINE logs are written to console.

Thus we can investigate flaky tests.
asolntsev added a commit that referenced this issue Aug 23, 2024
there is no need to initialize static Faker's vars in some specific way. They should stably work in any order.
asolntsev added a commit that referenced this issue Oct 16, 2024
Instead of two-three separate calls like `containsKey`, `get` and `put`, now we use a single `computeIfAbsent`.
asolntsev added a commit that referenced this issue Oct 16, 2024
Instead of two-three separate calls like `containsKey`, `get` and `put`, now we use a single `computeIfAbsent`.

P.S. I would like to also deny method `CopyOnWriteMap.put()`, but it's still used in one place (that was harder to refactor).
@asolntsev asolntsev linked a pull request Oct 16, 2024 that will close this issue
asolntsev added a commit that referenced this issue Oct 16, 2024
Instead of two-three separate calls like `containsKey`, `get` and `put`, now we use a single `computeIfAbsent`.

P.S. I would like to also deny method `CopyOnWriteMap.put()`, but it's still used in one place (that was harder to refactor).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment