Skip to content

[CRITICAL] Fallbacks are mutable during traversal #193

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

Closed
Aaronontheweb opened this issue Jan 24, 2020 · 3 comments · Fixed by #197
Closed

[CRITICAL] Fallbacks are mutable during traversal #193

Aaronontheweb opened this issue Jan 24, 2020 · 3 comments · Fixed by #197
Labels
akkadotnet-compat Legacy compatibility with Akka.Configuration bug
Milestone

Comments

@Aaronontheweb
Copy link
Member

Version: v1.3.1

Reproduction: #192

[Fact]
        public void Should_dump_HOCON_with_2_Fallback()
        {
            var myHocon1 = ConfigurationFactory.ParseString(@"akka{
                actor.provider = cluster
                deployment{
                    /foo {
                        dispatcher = bar
                    }
                }
            }");

            var myHocon2 = ConfigurationFactory.ParseString(@"akka{
                debug{
                    received  = on
                }
                actor.provider = cluster
                deployment{
                    /foo {
                        dispatcher = foo
                    }
                    /bar {
                        mailbox = ""myFoo""
                    }
                }
            }");

            var myHocon3 = ConfigurationFactory.ParseString(@"akka{
                remote{
                    transport = ""foo""
                }
            }");

            var fullHocon = myHocon1
                .SafeWithFallback(myHocon2)
                .SafeWithFallback(myHocon3);

            // bug reproduction
            //fullHocon.GetString("akka.remote.transport").Should().Be("foo");

            var dump = fullHocon.DumpConfig();
            dump.Should().Contain(myHocon1.PrettyPrint(2));
            dump.Should().Contain(myHocon2.PrettyPrint(2));
            dump.Should().Contain(myHocon3.PrettyPrint(2));
        }
    }

If I run this test, as-is, all of the text for each fallback will be correctly preserved and the test will pass and produce the following output:

HOCON0
{
  akka : {
    actor : {
      provider : cluster
    },
    deployment : {
      /foo : {
        dispatcher : bar
      }
    }
  }
}

------------
HOCON1
{
  akka : {
    debug : {
      received : on
    },
    actor : {
      provider : cluster
    },
    deployment : {
      /foo : {
        dispatcher : foo
      },
      /bar : {
        mailbox : "myFoo"
      }
    }
  }
}

------------
HOCON2
{
  akka : {
    remote : {
      transport : "foo"
    }
  }
}

This is the expected output.

However, if I uncomment the fullHocon.GetString("akka.remote.transport").Should().Be("foo") line... Here is what we end up with instead:

HOCON0
{
  akka : {
    actor : {
      provider : cluster
    },
    deployment : {
      /foo : {
        dispatcher : bar
      }
    }
  }
}

------------
HOCON1
{
  akka : {
    debug : {
      received : on
    },
    actor : {
      provider : cluster
    },
    deployment : {
      /foo : {
        dispatcher : bar
      },
      /bar : {
        mailbox : "myFoo"
      }
    }
  }
}

------------
HOCON2
{
  akka : {
    remote : {
      transport : "foo"
    },
    debug : {
      received : on
    },
    actor : {
      provider : cluster
    },
    deployment : {
      /foo : {
        dispatcher : bar
      },
      /bar : {
        mailbox : "myFoo"
      }
    }
  }
}

The test fails in this scenario because the fallback HOCON values are populated with the values from the head.

In other words, with the following HOCON chain:

hocon1 --> fallback1 --> fallback2

When I call hocon1.ToString("keyInFallback2") all of the values from hocon1 will be mapped onto fallback1 and all of the values from fallback1 will be mapped onto fallback2. As a result, we can have race conditions and all sorts of other weird errors in the Akka.NET test suite.

This aligns with exactly the bugs I'm trying to track down in akkadotnet/akka.net#4128

@Aaronontheweb Aaronontheweb added bug akkadotnet-compat Legacy compatibility with Akka.Configuration labels Jan 24, 2020
@Aaronontheweb Aaronontheweb added this to the 1.3.2 milestone Jan 24, 2020
@Aaronontheweb
Copy link
Member Author

Adding the text files from above:

hocon-correct.txt
hocon-bad.txt

Aaronontheweb added a commit to Aaronontheweb/HOCON that referenced this issue Jan 24, 2020
@IgorFedchenko
Copy link
Contributor

Wondering why this tests is passing...
Because using

var _ = fullHocon.Root.GetObject();

instead of

fullHocon.GetString("akka.remote.transport").Should().Be("foo");

also makes spec to fail.

So I thought that I have already fixed this, and added test for it. Will check what is the difference.

@IgorFedchenko
Copy link
Contributor

And even more interesting:

myHocon2.GetString("akka.deployment./foo.dispatcher").Should().Be("foo");

passes.

@Aaronontheweb Ah, got it.
So yes, Fallback objects, that are stored in config, are modified (merged with initial config value) - that's why they are cloned from original fallback objects when saved to Fallback property.
So original myHocon2 is cloned, and stays unmodified, while inner Fallback of myHocon1 object is modified.

This was by design, did not thought that this can be an issue. But it is easy to fix, I will submit PR right now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
akkadotnet-compat Legacy compatibility with Akka.Configuration bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants