Skip to content

Using forSchemaOptions inside forSchema causes a duplicate type name error #294

Description

@alonisser

I'm using avsc wrapped by schemaregistry Thanks for your hard word and good docs!

I'm trying to solve the long/bigint issue with the constraint I have only limited access directly to avsc. the wrapping library allows me to pass a forSchemaOptions configuration that is passed inside into an avsc wrapper and then invoked

 this.schema = Type.forSchema(schema, this.forSchemaOptions); (Type is from avsc)

I've found out that passing inside forSchemaOptions
{
registry: { long: longType } //Where longType is bigInt based long.__with as the docs refer to
}

But I've encountered a peculiar problem. if I pass a forSchemaOptions object with the registry or even an empty object {} without a registry

I get ONCE this error (the actual key is part of the schema auto generated by debezium/kafkaconnect)

"Error: duplicate type name: mongo_insights.zcdb_staging.dataitem.Key\n    at RecordType.Type (/home/alonisser/projects/zencity/zc-node-framework/node_modules/avsc/lib/types.js:107:17)\n    at new RecordType (/home/alonisser/projects/zencity/zc-node-framework/node_modules/avsc/lib/types.js:2099:8)\n    at /home/alonisser/projects/zencity/zc-node-framework/node_modules/avsc/lib/types.js:226:14\n    at Function.Type.forSchema (/home/alonisser/projects/zencity/zc-node-framework/node_modules/avsc/lib/types.js:227:7)\n

How can we work around this?

Activity

  1. alonisser commented on Apr 4, 2020

    @alonisser
    Author

    Just to be clear if I don't pass any options at all - not an empty object - nothing at all, I don't get this error and get errors when trying to actually work with the long (which is a bit later I think)

  2. mtth commented on Apr 7, 2020

    @mtth
    Owner

    Hi @alonisser. It looks like the issue comes from reusing options within the schema registry library. When an options object is passed in to Type.forSchema (even empty), its registry key will be populated with the newly instantiated types. This state persists between calls and will trigger a duplicate type name error when a later call tries to instantiate a type already registered.

    Due to this behavior, it's best to pass in new options when calling Type.forSchema on unrelated schemas. A possible solution for libraries wrapping avsc is to allow users to provide the parsing function itself, defaulting to Type.forSchema. The parseHook option in BlockDecoder is an example of this. Here, it sounds like this would require a change to the schema library, which I would encourage you to suggest.

    In the meantime, I think you can work around the error by adding a typeHook to the options. This hook will intercept the type generation logic, checking first if a type with the same name is already registered and returning it directly if so. For example something like:

    // Hook to add as `typeHook` key to `Type.forSchema` options.
    function typeHook(schema, opts) {
      let name = schema.name;
      if (!name) {
        return; // Not a named type, use default logic.
      }
      if (!~name.indexOf('.')) {
        // We need to qualify the type's name.
        const namespace = schema.namespace || opts.namespace;
        if (namespace) {
          name = `${namespace}.${name}`;
        }
      }
      // Return the type registered with the same name, if any.
      return opts.registry[name];
    }

    Let me know if this works for your use-case.

  3. alonisser commented on Apr 7, 2020

    @alonisser
    Author

    Thanks for the reply @mtth I'm trying to work with the schemalibrary lib, but currently can't get traction there.
    I've tried a direct typehook and failed to make it work, I'll try this and update

  4. alonisser commented on Apr 7, 2020

    @alonisser
    Author

    Thanks, it worked!

  5. NicholasGWK commented on Jul 22, 2020

    @NicholasGWK

    Sorry to resurrect this, but will the same duplication problem happen if I'm trying to pass in logicalTypes as an option instead of registry? I feel like it might but I'm having trouble stepping through and seeing where "the state is persisted between calls" like you mentioned!

    Thanks for any help :)

  6. mtth commented on Aug 1, 2020

    @mtth
    Owner

    Hi @NicholasGWK. Assuming you are using the same underlying library, you will face the issue described above as soon as you specify options. The state is persisted in the options object itself: when non-undefined, it will gain a registry attribute which spans calls.

    That being said, the typeHook snippet in #294 (comment) should allow you to work around it:

    const opts = {
      logicalTypes: {}, // Your logical types.
      typeHook, // The typehook from the comment above.
      // Any other options...
    };
  7. WaleedAshraf commented on Oct 29, 2021

    @WaleedAshraf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions