Azure / azure-functions-nodejs-worker

The Node.js worker for the Azure Functions runtime - https://functions.azure.com
MIT License
107 stars 44 forks source link

Table Storage Binding doesn't allow for access or filtering of Table Storage 'Timestamp' property #320

Open SKHolmes opened 4 years ago

SKHolmes commented 4 years ago

Investigative information

Upon retrieving the whole table via my bindings in a function that is triggered on a timer, (see below).

{
  "bindings": [
    {
      "name": "myTimer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "0 */1 * * * *"
    },
    {
      "name": "eventTable",
      "type": "table",
      "tableName": "HeartbeatEvents",
      "direction": "in"
    }
  ],
  "scriptFile": "../dist/src/heartbeat-scheduler/index.js"
}

I would expect that the property Timestamp which is a property maintained by the Storage Table server is included in the eventTable binding in the code and be accessible as a property of the array elements of the eventTable, i.e

context.bindings.eventTable.forEach((row) => {
    context.log(row.Timestamp);
  });

Instead it is undefined.

In the case of the storage table I am using a very basic set up like so, image

And when I run the function locally, it is not deployed yet, I get the following output through context logging,

[16/07/2020 1:51:00 am] Executing 'Functions.heartbeat-scheduler' (Reason='Timer fired at 2020-07-16T13:51:00.0093734+12:00', Id=9972164a-5a33-4f68-a240-95e6b7b6db20) [16/07/2020 1:51:00 am] Heartbeat Logging Triggered on: Thu Jul 16 2020 13:51:00 GMT+1200 [16/07/2020 1:51:00 am] [ { Status: 'received', TransactionId: '40574c28-0478-4aeb-a568-40cfea7b957c', PartitionKey: 'com.triquestra.cloudevents.salesinvoices.heartbeat', RowKey: 'tq-site_code-101' }, { Status: 'received', TransactionId: '50574c28-0478-4aeb-a568-40cfea7b957c', PartitionKey: 'com.triquestra.cloudevents.salesinvoices.heartbeat', RowKey: 'tq-site_code-102' } ] [16/07/2020 1:51:00 am] Row 0 has timestamp of: undefined [16/07/2020 1:51:00 am] Row 0 has new heartbeat [16/07/2020 1:51:00 am] Row 1 has timestamp of: undefined [16/07/2020 1:51:00 am] Row 1 has new heartbeat [16/07/2020 1:51:00 am] Executed 'Functions.heartbeat-scheduler' (Succeeded, Id=9972164a-5a33-4f68-a240-95e6b7b6db20) Note how Timestamp is excluded.

As this function is only local at the moment I do not have any function id information yet.

Reproduction steps

  1. Create a storage table and insert a record (Note that the timestamp field is automatically created and updated)
  2. Create a basic timer-triggered application. (This application was built off the example provided here.)
  3. Link the storage account associated with the table in your local settings.
  4. Try to access the table row's in the timerTrigger function and see that Timestamp is inaccessible.

Expected behavior

Expected that Timestamp is accessible for use in the function code.

Actual behavior

Any attempts to access Timestamp returns undefined.

Known workarounds

None known so far. Although I did try adding a filter to the binding to limit by Timestamp and it throws an error. (Accessing other valid properties within the table were fine.)

Related information

The function was built in typescript with the only dependency being moment.js. The source for the function is copied below for fast reproduction of the issue.

Source ```typescript import { AzureFunction, Context } from '@azure/functions'; import moment from 'moment'; import { triggerStartMessage } from '../resources/constants'; const timerTrigger: AzureFunction = async function ( context: Context, myTimer: any ): Promise { const now = moment(); const timeStamp = now.toString(); const fifteenMinutesAgo = now.subtract(15, 'minutes'); context.log(triggerStartMessage(timeStamp)); context.log(context.bindings.eventTable); const checkHeartbeat = (row, index) => { const isOldHeartbeat = fifteenMinutesAgo.isAfter(row.Timestamp); context.log(`Row ${index} has timestamp of: ${row.Timestamp}`); if (isOldHeartbeat) { context.log(`Row ${index} has old heartbeat`); } else { context.log(`Row ${index} has new heartbeat`); } } context.bindings.eventTable.map(checkHeartbeat); }; export default timerTrigger; ```
mikkark commented 3 years ago

I just bumped into this myself too. I thought about doing my custom backup solution for some data in Table Storage using a simple timed Azure Function in TS, but I will miss the timestamp field. It is not exactly a showstopper for me, but very strange indeed.

alrod commented 2 years ago

I was able to reproduce the issue in Javascript as well as in Python. Probably it's related to Timestamp protopbuf type and it's truncated somehow during sending grpc messge from function host to language worker.

DateTime property with another name works fine.