Hey! I’ve got a test suite that’s failing inside containers but passing on the host machine.
It’s written in Go and uses time.Now()
to get nanosecond precision timestamps. The tests are basically doing a custom json.Marshal and json.Unmarshal in order to convert the timestamp to an integer and back again - it all works nicely in our system (if anyone’s interested why, it’s to save bytes on large BSON payloads with tons of time-series data, timestamps are just 8 bytes whereas a string representation can be up to 55 bytes!)
So the weird thing here is that time.Now()
is actually returning nanosecond precision because it uses it in the test case to set the expected
value (as you’ll see in the diff) but the conversion code doesn’t appear to be functioning the same inside the container.
DataStructure{Time:time.Time{wall:0xbe680503db9047f2, ext:3924085, loc:(*time.Location)(0x9fe260)}, <other fields...>} (expected)
DataStructure{Time:time.Time{wall:0x1b9047f2, ext:63641326479, loc:(*time.Location)(0x9fe260)}, <other fields...>} (actual)
You can see the expected
value contains the full 64 bit timestamp bytes, no zeroes,
And here’s a larger breakdown from the awesome Testify library:
Diff:
--- Expected
+++ Actual
@@ -1,3 +1,3 @@
(shared.TaskPosition) {
- Time: (time.Time) 2017-09-18 10:14:39.462440434 +0000 UTC m=+0.003924085,
+ Time: (time.Time) 2017-09-18 10:14:39.462440434 +0000 UTC,
TaskID: (shared.TaskID) (len=2) "t1",
So in the test, the expected
value is being loaded correctly but somewhere in the conversion code, it’s not kicking out the same value again - and this only happens inside docker. I actually have no idea what m=+0.003924085
is, I assumed the .462440434
contains the granularity for nanoseconds. Why is this m
value missing from the actual
result?
The conversion code looks like this:
func (tp *DataStructure) UnmarshalJSON(b []byte) error {
// create a dummy data structure of the expected format, all other fields are composed in and
// the `Time` field is overwritten with an int64 instead of a time.Time as in the alias
type alias DataStructure
tmp := struct {
Time int64 `json:"t"`
*alias
}{}
// Unmarshal the fake structure, processing Time as an int64
err := json.Unmarshal(b, &tmp)
if err != nil {
return err
}
// convert the Time in the temporary structure to a time.Time and assign it to the real one
tp.Time := time.Unix(0, tmp.Time)
// assign all other fields to `tp`
tp.OtherFields = tmp.OtherFields
// etc...
return nil
}
I understand this may be a Go question but the fact that it only happens in a container is odd to me. Hopefully someone can help find a solution!
Thanks!