Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Saving an empty Buffer throws an error. #755

Closed
vicb opened this issue Nov 17, 2020 · 11 comments · Fixed by #1072
Closed

Saving an empty Buffer throws an error. #755

vicb opened this issue Nov 17, 2020 · 11 comments · Fixed by #1072
Assignees
Labels
api: datastore Issues related to the googleapis/nodejs-datastore API. priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.

Comments

@vicb
Copy link
Contributor

vicb commented Nov 17, 2020

Environment details

  • OS: MacOs
  • Node.js version: v14.4.0
  • npm version: 6.14.8
  • @google-cloud/datastore version: 6.3.0

Steps to reproduce

Execute the following code:

async function saveBlob() {
  const key = datastore.key(['TEST']);
  
  await datastore.save({
    key: key,
    data: {
      name: 'test',
      blob: Buffer.from([])
    },
  });
}

It will trigger an error: Error: 3 INVALID_ARGUMENT: The value "blob" does not contain a value.

I expect to be able to save an empty Buffer without error.

If you change the blob value to Buffer.from([1, 2, 3]) the error disappears.

My use case is that I have a protocol buffer composed of repeated fields only:

message LiveTrack {
  optional int64 id = 1 [jstype = JS_NUMBER];
  optional string name = 2;   
  repeated float lat = 3;
  repeated float lon = 4;
  repeated int32 alt = 5;
  repeated int32 time_sec = 6;
  repeated uint32 flags = 7;
  map<uint32, LiveExtra> extra = 8;
}

It could happen that all repeated fields (including the map) are empty. Because the optional field are not populated, the proto would serialize to an empty Buffer.

Workaround

The workaround would be to set the value to null when the Buffer size is 0.
This requires extra code both while saving (to replace the Buffer with null) and while loading (to replace null with the default message).

Other

The wording of the message could be improved: The value "blob" does not contain a value., i.e. use field or key instead of the first "value".

@product-auto-label product-auto-label bot added the api: datastore Issues related to the googleapis/nodejs-datastore API. label Nov 17, 2020
@vicb
Copy link
Contributor Author

vicb commented Nov 17, 2020

EDIT: The actual issue in protobuf-js, while this should also resolve the issue it probably better to fix protobufjs (see below for details)

I have update encodeValue to read like:

        if (value instanceof Buffer) {
            // was valueProto.blobValue = buffer;    
            valueProto.blobValue = value.length == 0 ? '' : value;
            return valueProto;
        }

And it fixes the issue:

  • The column is save as a Blob without vale (verified in the Cloud Console),
  • Reading the column returns an empty buffer.

Anyway this code looks like it fixes the issue.

An other way to fix would be valueProto.blobValue = buffer.toString('base64');.

I don't know the code well enough to determine if this is the best possible fix.
What do you think ?

@bcoe bcoe added type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. priority: p2 Moderately-important priority. Fix may not be included in next release. labels Nov 17, 2020
@vicb
Copy link
Contributor Author

vicb commented Nov 20, 2020

I have done some digging into this bug:

Calling datastore.save(...) ends up @grpc/proto-loader createSerializer

function createSerializer(cls: Protobuf.Type): Serialize<object> {
  return function serialize(arg: object): Buffer {
    const message = cls.fromObject(arg);
    return cls.encode(message).finish() as Buffer;
  };
}

The problems seems to be in const message = cls.fromObject(arg);

The arg is:

mode:'NON_TRANSACTIONAL'
mutations:(1) [
  0: {
    upsert:{
      key:{path: Array(1)},
      properties:{
        name: {stringValue: 'test-null'},
        track: {blobValue: Buffer(0), excludeFromIndexes: true}
      }
    }
]

But the message variable has lost the value:

upsert: Entity {
  key:Key {path: Array(1)}
  properties: {
    name:Value {stringValue: 'test-null'}
    track:Value {excludeFromIndexes: true}
  }
}

There should be a blobValue: Buffer(0) for the track.

When the Buffer is not empty the message variable would look like:

upsert:Entity {
  key:Key {path: Array(1)}
  properties:{
    name:Value {stringValue: 'test-null'}
    track:Value {blobValue: Buffer(1), excludeFromIndexes: true}
  }
}

Here there is a blobValue for the track which is the expected result.

@vicb
Copy link
Contributor Author

vicb commented Nov 20, 2020

@vicb
Copy link
Contributor Author

vicb commented Nov 20, 2020

Patching protobufjs/protobuf.js#1500 did resolve the issue.

I'll try to move that PR forward and then propagate the fix through the dependencies.

@yoshi-automation yoshi-automation added 🚨 This issue needs some love. and removed 🚨 This issue needs some love. labels Feb 18, 2021
@yoshi-automation yoshi-automation added the 🚨 This issue needs some love. label May 17, 2021
@crwilcox
Copy link
Contributor

What work remains to close this issue?

@vicb
Copy link
Contributor Author

vicb commented Jun 21, 2021

See protobufjs/protobuf.js#1514 (review), the proper fix in protobuf.js should be in v7

The current nodejs-datastore code uses a workaround that could be removed once protobuf.js is fixed - that v7 is released and nodejs-datastore is updated to use this dep.

/cc @alexander-fenster

@crwilcox
Copy link
Contributor

crwilcox commented Jul 23, 2021

Update v7.0 of protobufjs is staged but not relased.

protobufjs/protobuf.js#1519

@crwilcox crwilcox added the external This issue is blocked on a bug with the actual product. label Jul 27, 2021
@yoshi-automation yoshi-automation removed the 🚨 This issue needs some love. label Jul 27, 2021
@meredithslota
Copy link
Contributor

https://github.com/protobufjs/protobuf.js/releases shows releases for 7.x+ — checking to see if we've picked these up yet.

@meredithslota meredithslota removed the external This issue is blocked on a bug with the actual product. label Sep 20, 2022
@meredithslota
Copy link
Contributor

The current minimum version of google-gax required is 3.3.0, and it looks like protobufjs v7 was required in v3.1.4, so we should be good to go to unwind this workaround. I've removed the "external" label accordingly.

@danieljbruce
Copy link
Contributor

Seems to run ok without workaround. Will create a PR and a test.

@danieljbruce
Copy link
Contributor

#1072

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: datastore Issues related to the googleapis/nodejs-datastore API. priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants