Search in sources :

Example 1 with SchemaTooLargeException

use of io.confluent.kafka.schemaregistry.exceptions.SchemaTooLargeException in project schema-registry by confluentinc.

the class KafkaSchemaRegistry method register.

@Override
public int register(String subject, Schema schema, boolean normalize) throws SchemaRegistryException {
    try {
        checkRegisterMode(subject, schema);
        // Ensure cache is up-to-date before any potential writes
        kafkaStore.waitUntilKafkaReaderReachesLastOffset(subject, kafkaStoreTimeoutMs);
        int schemaId = schema.getId();
        ParsedSchema parsedSchema = canonicalizeSchema(schema, schemaId < 0, normalize);
        // see if the schema to be registered already exists
        SchemaIdAndSubjects schemaIdAndSubjects = this.lookupCache.schemaIdAndSubjects(schema);
        if (schemaIdAndSubjects != null && (schemaId < 0 || schemaId == schemaIdAndSubjects.getSchemaId())) {
            if (schemaIdAndSubjects.hasSubject(subject) && !isSubjectVersionDeleted(subject, schemaIdAndSubjects.getVersion(subject))) {
                // return only if the schema was previously registered under the input subject
                return schemaIdAndSubjects.getSchemaId();
            } else {
                // need to register schema under the input subject
                schemaId = schemaIdAndSubjects.getSchemaId();
            }
        }
        // determine the latest version of the schema in the subject
        List<SchemaValue> allVersions = getAllSchemaValues(subject);
        Collections.reverse(allVersions);
        List<SchemaValue> deletedVersions = new ArrayList<>();
        List<ParsedSchema> undeletedVersions = new ArrayList<>();
        int newVersion = MIN_VERSION;
        for (SchemaValue schemaValue : allVersions) {
            newVersion = Math.max(newVersion, schemaValue.getVersion() + 1);
            if (schemaValue.isDeleted()) {
                deletedVersions.add(schemaValue);
            } else {
                ParsedSchema undeletedSchema = parseSchema(getSchemaEntityFromSchemaValue(schemaValue));
                if (parsedSchema.references().isEmpty() && !undeletedSchema.references().isEmpty() && parsedSchema.deepEquals(undeletedSchema)) {
                    // This handles the case where a schema is sent with all references resolved
                    return schemaValue.getId();
                }
                undeletedVersions.add(undeletedSchema);
            }
        }
        Collections.reverse(undeletedVersions);
        final List<String> compatibilityErrorLogs = isCompatibleWithPrevious(subject, parsedSchema, undeletedVersions);
        final boolean isCompatible = compatibilityErrorLogs.isEmpty();
        if (normalize) {
            parsedSchema = parsedSchema.normalize();
        }
        // Allow schema providers to modify the schema during compatibility checks
        schema.setSchema(parsedSchema.canonicalString());
        schema.setReferences(parsedSchema.references());
        if (isCompatible) {
            // save the context key
            QualifiedSubject qs = QualifiedSubject.create(tenant(), subject);
            if (qs != null && !DEFAULT_CONTEXT.equals(qs.getContext())) {
                ContextKey contextKey = new ContextKey(qs.getTenant(), qs.getContext());
                if (kafkaStore.get(contextKey) == null) {
                    ContextValue contextValue = new ContextValue(qs.getTenant(), qs.getContext());
                    kafkaStore.put(contextKey, contextValue);
                }
            }
            // assign a guid and put the schema in the kafka store
            if (schema.getVersion() <= 0) {
                schema.setVersion(newVersion);
            }
            SchemaKey schemaKey = new SchemaKey(subject, schema.getVersion());
            if (schemaId >= 0) {
                checkIfSchemaWithIdExist(schemaId, schema);
                schema.setId(schemaId);
                kafkaStore.put(schemaKey, new SchemaValue(schema));
            } else {
                int retries = 0;
                while (retries++ < kafkaStoreMaxRetries) {
                    int newId = idGenerator.id(new SchemaValue(schema));
                    // Verify id is not already in use
                    if (lookupCache.schemaKeyById(newId, subject) == null) {
                        schema.setId(newId);
                        if (retries > 1) {
                            log.warn(String.format("Retrying to register the schema with ID %s", newId));
                        }
                        kafkaStore.put(schemaKey, new SchemaValue(schema));
                        break;
                    }
                }
                if (retries >= kafkaStoreMaxRetries) {
                    throw new SchemaRegistryStoreException("Error while registering the schema due " + "to generating an ID that is already in use.");
                }
            }
            for (SchemaValue deleted : deletedVersions) {
                if (deleted.getId().equals(schema.getId()) && deleted.getVersion().compareTo(schema.getVersion()) < 0) {
                    // Tombstone previous version with the same ID
                    SchemaKey key = new SchemaKey(deleted.getSubject(), deleted.getVersion());
                    kafkaStore.put(key, null);
                }
            }
            return schema.getId();
        } else {
            throw new IncompatibleSchemaException(compatibilityErrorLogs.toString());
        }
    } catch (EntryTooLargeException e) {
        throw new SchemaTooLargeException("Write failed because schema is too large", e);
    } catch (StoreTimeoutException te) {
        throw new SchemaRegistryTimeoutException("Write to the Kafka store timed out while", te);
    } catch (StoreException e) {
        throw new SchemaRegistryStoreException("Error while registering the schema in the" + " backend Kafka store", e);
    }
}
Also used : QualifiedSubject(io.confluent.kafka.schemaregistry.utils.QualifiedSubject) ArrayList(java.util.ArrayList) SchemaRegistryStoreException(io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryStoreException) SchemaString(io.confluent.kafka.schemaregistry.client.rest.entities.SchemaString) SchemaRegistryStoreException(io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryStoreException) StoreException(io.confluent.kafka.schemaregistry.storage.exceptions.StoreException) IncompatibleSchemaException(io.confluent.kafka.schemaregistry.exceptions.IncompatibleSchemaException) EntryTooLargeException(io.confluent.kafka.schemaregistry.storage.exceptions.EntryTooLargeException) SchemaTooLargeException(io.confluent.kafka.schemaregistry.exceptions.SchemaTooLargeException) StoreTimeoutException(io.confluent.kafka.schemaregistry.storage.exceptions.StoreTimeoutException) ParsedSchema(io.confluent.kafka.schemaregistry.ParsedSchema) SchemaRegistryTimeoutException(io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryTimeoutException)

Example 2 with SchemaTooLargeException

use of io.confluent.kafka.schemaregistry.exceptions.SchemaTooLargeException in project schema-registry by confluentinc.

the class SubjectVersionsResource method register.

@POST
@DocumentedName("registerSchema")
@PerformanceMetric("subjects.versions.register")
@Operation(summary = "Register schema under a subject", description = "Register a new schema under the specified subject. If successfully " + "registered, this returns the unique identifier of this schema in the registry. The " + "returned identifier should be used to retrieve this schema from the schemas resource " + "and is different from the schema's version which is associated with the subject. If the " + "same schema is registered under a different subject, the same identifier will be " + "returned. However, the version of the schema may be different under different subjects." + "\nA schema should be compatible with the previously registered schema or schemas (if " + "there are any) as per the configured compatibility level. The configured compatibility " + "level can be obtained by issuing a GET http:get:: /config/(string: subject). If that " + "returns null, then GET http:get:: /config\n" + "When there are multiple instances of Schema Registry running in the same cluster, the " + "schema registration request will be forwarded to one of the instances designated as " + "the primary. If the primary is not available, the client will get an error code " + "indicating that the forwarding has failed.", responses = { @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = RegisterSchemaResponse.class))), @ApiResponse(responseCode = "409", description = "Incompatible schema"), @ApiResponse(responseCode = "422", description = "Error code 42201 -- Invalid schema or " + "schema type"), @ApiResponse(responseCode = "500", description = "Error code 50001 -- Error in the backend data store\n" + "Error code 50002 -- Operation timed out\n" + "Error code 50003 -- Error while forwarding the request to the primary") })
public void register(@Suspended final AsyncResponse asyncResponse, @Context HttpHeaders headers, @Parameter(description = "Name of the subject", required = true) @PathParam("subject") String subjectName, @Parameter(description = "Whether to register the normalized schema") @QueryParam("normalize") boolean normalize, @Parameter(description = "Schema", required = true) @NotNull RegisterSchemaRequest request) {
    log.info("Registering new schema: subject {}, version {}, id {}, type {}, schema size {}", subjectName, request.getVersion(), request.getId(), request.getSchemaType(), request.getSchema() == null ? 0 : request.getSchema().length());
    if (subjectName != null && (CharMatcher.javaIsoControl().matchesAnyOf(subjectName) || QualifiedSubject.create(this.schemaRegistry.tenant(), subjectName).getSubject().equals(GLOBAL_RESOURCE_NAME))) {
        throw Errors.invalidSubjectException(subjectName);
    }
    subjectName = QualifiedSubject.normalize(schemaRegistry.tenant(), subjectName);
    Map<String, String> headerProperties = requestHeaderBuilder.buildRequestHeaders(headers, schemaRegistry.config().whitelistHeaders());
    Schema schema = new Schema(subjectName, request.getVersion() != null ? request.getVersion() : 0, request.getId() != null ? request.getId() : -1, request.getSchemaType() != null ? request.getSchemaType() : AvroSchema.TYPE, request.getReferences(), request.getSchema());
    int id;
    try {
        id = schemaRegistry.registerOrForward(subjectName, schema, normalize, headerProperties);
    } catch (IdDoesNotMatchException e) {
        throw Errors.idDoesNotMatchException(e);
    } catch (InvalidSchemaException e) {
        throw Errors.invalidSchemaException(e);
    } catch (SchemaTooLargeException e) {
        throw Errors.schemaTooLargeException("Register operation failed because schema is too large");
    } catch (OperationNotPermittedException e) {
        throw Errors.operationNotPermittedException(e.getMessage());
    } catch (SchemaRegistryTimeoutException e) {
        throw Errors.operationTimeoutException("Register operation timed out", e);
    } catch (SchemaRegistryStoreException e) {
        throw Errors.storeException("Register schema operation failed while writing" + " to the Kafka store", e);
    } catch (SchemaRegistryRequestForwardingException e) {
        throw Errors.requestForwardingFailedException("Error while forwarding register schema request" + " to the leader", e);
    } catch (IncompatibleSchemaException e) {
        throw Errors.incompatibleSchemaException("Schema being registered is incompatible with" + " an earlier schema for subject \"" + subjectName + "\", details: " + e.getMessage(), e);
    } catch (UnknownLeaderException e) {
        throw Errors.unknownLeaderException("Leader not known.", e);
    } catch (SchemaRegistryException e) {
        throw Errors.schemaRegistryException("Error while registering schema", e);
    }
    RegisterSchemaResponse registerSchemaResponse = new RegisterSchemaResponse();
    registerSchemaResponse.setId(id);
    asyncResponse.resume(registerSchemaResponse);
}
Also used : SchemaRegistryRequestForwardingException(io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryRequestForwardingException) UnknownLeaderException(io.confluent.kafka.schemaregistry.exceptions.UnknownLeaderException) Schema(io.confluent.kafka.schemaregistry.client.rest.entities.Schema) AvroSchema(io.confluent.kafka.schemaregistry.avro.AvroSchema) ArraySchema(io.swagger.v3.oas.annotations.media.ArraySchema) SchemaRegistryStoreException(io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryStoreException) SchemaRegistryException(io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryException) RegisterSchemaResponse(io.confluent.kafka.schemaregistry.client.rest.entities.requests.RegisterSchemaResponse) IncompatibleSchemaException(io.confluent.kafka.schemaregistry.exceptions.IncompatibleSchemaException) InvalidSchemaException(io.confluent.kafka.schemaregistry.exceptions.InvalidSchemaException) SchemaTooLargeException(io.confluent.kafka.schemaregistry.exceptions.SchemaTooLargeException) IdDoesNotMatchException(io.confluent.kafka.schemaregistry.exceptions.IdDoesNotMatchException) OperationNotPermittedException(io.confluent.kafka.schemaregistry.exceptions.OperationNotPermittedException) SchemaRegistryTimeoutException(io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryTimeoutException) PerformanceMetric(io.confluent.rest.annotations.PerformanceMetric) POST(javax.ws.rs.POST) Operation(io.swagger.v3.oas.annotations.Operation)

Aggregations

IncompatibleSchemaException (io.confluent.kafka.schemaregistry.exceptions.IncompatibleSchemaException)2 SchemaRegistryStoreException (io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryStoreException)2 SchemaRegistryTimeoutException (io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryTimeoutException)2 SchemaTooLargeException (io.confluent.kafka.schemaregistry.exceptions.SchemaTooLargeException)2 ParsedSchema (io.confluent.kafka.schemaregistry.ParsedSchema)1 AvroSchema (io.confluent.kafka.schemaregistry.avro.AvroSchema)1 Schema (io.confluent.kafka.schemaregistry.client.rest.entities.Schema)1 SchemaString (io.confluent.kafka.schemaregistry.client.rest.entities.SchemaString)1 RegisterSchemaResponse (io.confluent.kafka.schemaregistry.client.rest.entities.requests.RegisterSchemaResponse)1 IdDoesNotMatchException (io.confluent.kafka.schemaregistry.exceptions.IdDoesNotMatchException)1 InvalidSchemaException (io.confluent.kafka.schemaregistry.exceptions.InvalidSchemaException)1 OperationNotPermittedException (io.confluent.kafka.schemaregistry.exceptions.OperationNotPermittedException)1 SchemaRegistryException (io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryException)1 SchemaRegistryRequestForwardingException (io.confluent.kafka.schemaregistry.exceptions.SchemaRegistryRequestForwardingException)1 UnknownLeaderException (io.confluent.kafka.schemaregistry.exceptions.UnknownLeaderException)1 EntryTooLargeException (io.confluent.kafka.schemaregistry.storage.exceptions.EntryTooLargeException)1 StoreException (io.confluent.kafka.schemaregistry.storage.exceptions.StoreException)1 StoreTimeoutException (io.confluent.kafka.schemaregistry.storage.exceptions.StoreTimeoutException)1 QualifiedSubject (io.confluent.kafka.schemaregistry.utils.QualifiedSubject)1 PerformanceMetric (io.confluent.rest.annotations.PerformanceMetric)1