Litedb: V5 beta fail to serialize double on ARM CPU

Created on 28 Nov 2019  路  10Comments  路  Source: mbdavid/LiteDB

Hi, I observe the following error in logs of my app:

System.DataMisalignedException: A datatype misalignment was detected in a load or store instruction.
   at LiteDB.BufferExtensions.ToBytes(Double value, Byte[] array, Int32 startIndex)
   at LiteDB.Engine.BufferWriter.WriteNumber[T](T value, Action`3 toBytes, Int32 size)
   at LiteDB.Engine.BufferWriter.Write(Double value)
   at LiteDB.Engine.BufferWriter.WriteElement(String key, BsonValue value)
   at LiteDB.Engine.BufferWriter.WriteDocument(BsonDocument value, Boolean recalc)
   at LiteDB.Engine.DataService.Insert(BsonDocument doc)
   at LiteDB.Engine.LiteEngine.InsertDocument(Snapshot snapshot, BsonDocument doc, BsonAutoId autoId, IndexService indexer, DataService data)
   at LiteDB.Engine.LiteEngine.<>c__DisplayClass8_0.<Insert>b__0(TransactionService transaction)
   at LiteDB.Engine.LiteEngine.AutoTransaction[T](Func`2 fn)
   at LiteDB.Engine.LiteEngine.Insert(String collection, IEnumerable`1 docs, BsonAutoId autoId)
   at LiteDB.LiteCollection`1.Insert(T document)

It happens everytime on types with non default double values. Here's the definition of the type:

public class Checkpoint : ICheckpoint
    {
        private static long nextSequence;
        public DateTime Timestamp { get; set; } = new DateTime(0, DateTimeKind.Utc);
        public string RiderId { get; set; }
        public long Id { get; set; }

        public DateTime LastSeen { get; set; } = new DateTime(0, DateTimeKind.Utc);
        public int Count { get; set; } = 1;
        public bool Aggregated { get; set; }
        public bool IsManual { get; set; }
        public double Rps { get; set; }

        public Checkpoint()
        {
        }

        public Checkpoint(string riderId, DateTime? timestamp = null)
        {
            Id = Interlocked.Increment(ref nextSequence);
            RiderId = riderId;
            LastSeen = Timestamp = timestamp ?? new DateTime(0, DateTimeKind.Utc);
        }

        public override string ToString()
        {
            return $"{RiderId} Ts:{Timestamp:t}";
        }

        private sealed class TimestampRelationalComparer : IComparer<Checkpoint>
        {
            public int Compare(Checkpoint x, Checkpoint y)
            {
                if (ReferenceEquals(x, y)) return 0;
                if (ReferenceEquals(null, y)) return 1;
                if (ReferenceEquals(null, x)) return -1;
                return x.Timestamp.CompareTo(y.Timestamp);
            }
        }

        public static IComparer<Checkpoint> TimestampComparer { get; } = new TimestampRelationalComparer();
    }

If the Rps value is zero, the object would serialize and deserialize without problems.
Unfortunately, I can't create a minimal reproduction yet. A simple to save and load one item is not failing. So probably particular value makes the difference

bug

All 10 comments

Issue is harder to track than I excepted.
If you just login to an ARM machine (orange PI Prime with Armbian Buster with Linux 5.3.9-sunxi64) download and build the project it will work fine. I tried my own small test app and also ran LiteDB tests there.
But when I build docker image on my Windows AMD64 machine and then use that image on Orange PI I get the error.
This is the build image that reproduce the error: docker run -it --rm maxbl4/litedb-test-armhf
And this is a repo with sources: https://github.com/maxbl4/LiteDb-Test

More tests show, that difference is in the build process. Looks like dotnet creates different binaries while building on x86 and arm platforms

I see several workarounds:

  1. Create a build farm and performa ARM builds on native hardware. Which is slow and requires separate hardware
  2. Use Decimal instead of Double. That works
  3. Just use Int32 - for my project that is fine

Hi @maxbl4, first of all, sorry for the late response. However, I'm glad you were able to find possible workarounds for your issue. I, myself, don't have enough knowledge about building on ARM devices, but the second workaround is kinda a headscratcher for me as to why it would work with decimals, but not with doubles.

But as far as I've read the entire issue log, I don't think the core issue lies within our library and therefor I'll be closing this issue. I also want to point out that there's a new version of LiteDB available in the meantime (v5 RC) so you might want to check that out as well, but keep in mind that the dataformat is incompatible with the one used in the beta, so you'll need to recreate your database.

@maxbl4 There was a change to how floats and doubles are serialized to byte[]. This should fix this issue, however I don't have access to an ARM-based machine to test it. Would you mind testing it? The changes are not on NuGet yet, but they are commited to master.

Seems like I might have been wrong, my bad. I'll reopen the issue till @maxbl4 can confirm it's actually fixed, because I don't have an ARM device either :)
You may close it if it resolves this issue.

I had the same issue on an old raspberry pi. I compiled the source from master and linked that to my project. Upgrade of an 188MB databse went fine and app is running again. So it seems the issue is indeed closed. Thanks for the fix.

Hi, confirm, that build from latest master has fixed the bug. Thanks!

Closing the issue again as it has been confirmed that the fix resolves the original issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

RealBlazeIt picture RealBlazeIt  路  3Comments

manny42 picture manny42  路  3Comments

kuiperzone picture kuiperzone  路  4Comments

dangershony picture dangershony  路  3Comments

axelgenus picture axelgenus  路  3Comments