dbos-inc / dbos-transact

The TypeScript framework for backends that scale
https://docs.dbos.dev
MIT License
335 stars 22 forks source link

DBOS Transact should support Date type parameter/output #450

Closed qianl15 closed 2 months ago

qianl15 commented 3 months ago

The problem is when we run JSON.stringify the Date will be converted into string, and when we try to recover workflow or replay a workflow there's no way to deserialize it back to Date.

We need to handle Date type in the DBOSReplacer and DBOSReviver https://github.com/dbos-inc/dbos-transact/blob/main/src/utils.ts So we can properly serialize and deserialize date. Then use the Replacer and Reviver everywhere we use JSON.stringify() and JSON.parse()

devhawk commented 3 months ago

cc @demetris-manikas

demetris-manikas commented 3 months ago

Can someone please clarify about which JSON.stringify/parse pair is this about? I guess (https://github.com/dbos-inc/dbos-transact/blob/8e9d254a4b7852196c1f4dea91d0aeaf77b8c984/src/workflow.ts#L210) and (https://github.com/dbos-inc/dbos-transact/blob/8e9d254a4b7852196c1f4dea91d0aeaf77b8c984/src/workflow.ts#L159) but not sure.

qianl15 commented 3 months ago

We need to add replacer and reviver whenever we record/retrieve stored inputs/outputs.

demetris-manikas commented 3 months ago

I got a POC on this using lodash for deep cloning.
json.stringify.parse.txt

The prefix should definitely be something other than date: but at least it's working

Let me know if this kind of solution is viable.

The script also demonstrates that there is no way of telling if a value is a Date in the JSON.stringify replacer

demetris-manikas commented 3 months ago

Forgot to mention. To run the POC drop the file in the root directory of the project Rename it to .js Run npm install lodash Run node json.stringify.parse

qianl15 commented 3 months ago

I think you'll be able to get the actual object by using this[key] instead of value in a replacer function. Source: https://stackoverflow.com/a/54037861

Doc: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter

As a function, it takes two parameters: the key and the value being stringified. The object in which the key was found is provided as the replacer's this context.

Here's an example to use the replacer in our case (based on the code you provided):

let obj = {
    num: '2',
    dates: {
        date1: new Date(2023, 10, 2, 23, 12, 200),
        date2: new Date(2024, 4, 1, 13, 11, 223)
    }
}

var replacer = function(key, value) {
    console.log(this[key])
    console.log(typeof this[key]) // This will be Object for a Date value
    console.log(typeof value) // This will be string for a Date value
    console.log(Object.prototype.toString.call(this[key]) === '[object Date]') // This will be true for a Date value

    if (this[key] instanceof Date) { // or we can use Object.prototype.toString.call(this[key]) === '[object Date]'
       return 'date:' + this[key].toUTCString();
    }

    return value;
 }

let str = JSON.stringify(obj, replacer);
console.log(str)
kraftp commented 2 months ago

Addressed in https://github.com/dbos-inc/dbos-transact/pull/490