Skip to content

Silent switching of key generators should be denied at least in PerformSafely upgrade mode #153

Open
@alex-kulakov

Description

@alex-kulakov

Changing type of primary key column also switches key generator. This can cause problems in case when switching is from more usable generator to less usable one. Possible scenario is below.

If original model looks like so (V1):

    [HierarchyRoot]
    public class IntPkEntity : Entity
    {
      [Field, Key]
      public int Id { get; private set; }
      [Field]
      public DateTime CreationDate { get; set; }
    }

    [HierarchyRoot]
    public class LongPkEntity : Entity
    {
      [Field, Key]
      public long Id { get; private set; }
      [Field]
      public DateTime CreationDate { get; set; }
    }

    [HierarchyRoot]
    public class TypeSwithchingEntity : Entity
    {
      [Field, Key]
      public int Id { get; private set; }
      [Field]
      public DateTime CreationDate { get; set; }
    }

and we upgrade primary key of one type to long (V2)

    [HierarchyRoot]
    public class TypeSwithchingEntity : Entity
    {
      [Field, Key]
      public long Id { get; private set; }

      [Field]
      public DateTime CreationDate { get; set; }
    }

then we can face problem shown in the test

    [Test]
    public void MainTest()
    {
      var initConfig = GetDomainConfiguration();
      initConfig.Types.Register(typeof(V1.IntPkEntity));
      initConfig.Types.Register(typeof(V1.LongPkEntity));
      initConfig.Types.Register(typeof(V1.TypeSwithchingEntity));
      initConfig.UpgradeMode = DomainUpgradeMode.Recreate;

      using (var domain = Domain.Build(initConfig))
      using (var session = domain.OpenSession())
      using (var tx = session.OpenTransaction()) {
        for (var i = 0; i < 512; i++) { // int generator is used more frequently
          _ = new V1.IntPkEntity() { CreationDate = DateTime.UtcNow };
          _ = new V1.TypeSwithchingEntity() { CreationDate = DateTime.UtcNow };
        }
        for (var i = 0; i < 128; i++) {
          _ = new V1.LongPkEntity() { CreationDate = DateTime.UtcNow };
        }
        for (var i = 0; i < 128; i++) {
          _ = new V1.TypeSwithchingEntity() { CreationDate = DateTime.UtcNow };
        }
        tx.Complete();
      }

      var upgradeConfig = GetDomainConfiguration();
      upgradeConfig.Types.Register(typeof(V2.IntPkEntity));
      upgradeConfig.Types.Register(typeof(V2.LongPkEntity));
      upgradeConfig.Types.Register(typeof(V2.TypeSwithchingEntity));
      upgradeConfig.UpgradeMode = DomainUpgradeMode.PerformSafely;

      using (var domain = Domain.Build(upgradeConfig))
      using (var session = domain.OpenSession())
      using (var tx = session.OpenTransaction()) {
        _ = new V2.TypeSwithchingEntity() { CreationDate = DateTime.UtcNow };
        _ = new V2.TypeSwithchingEntity() { CreationDate = DateTime.UtcNow };
        session.SaveChanges();
      }
    }

After successful upgrade any new TypeSwitchingEntity may intersect with existing rows in table.

I'm pretty sure we should not allow such things to happen.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions