KB Article #183156

Memory consumption and export optimizations for OAS3 APIs

Problem


Prior to API Management February 2024 update, API Manager would resolve all referenced entities during the import of an OAS3 API and would store exploded instances of each referenced entity in memory for every $ref encountered.



For example, assuming the following OAS3 definition is imported into API Manager:


{
    "openapi": "3.0.2",
    "info": { "title": "Test API", "version": "0.0.1" },
    "servers": [{ "url": "http://localhost:8080" }],
    "paths": {
        "/test": {
            "get": {
                "operationId": "test",
                "parameters": [{
                        "name": "id",
                        "in": "query",
                        "schema": { "$ref": "#/components/schemas/id" }
                    },
                    {
                        "name": "apiId",
                        "in": "query",
                        "schema": { "$ref": "#/components/schemas/id" }
                }],
                "responses": {
                    "200": { "description": "Success" }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "id": { "type": "string", "format": "uuid", "maxLength": 36, "minLength": 36 }
        }
    }
}



The following definition would exist in memory after parsing:


{
    "openapi": "3.0.2",
    "info": { "title": "Test API", "version": "0.0.1" },
    "servers": [{ "url": "http://localhost:8080" }],
    "paths": {
        "/test": {
            "get": {
                "operationId": "test",
                "parameters": [{
                    "name": "id",
                    "in": "query",
                    "schema": { "type": "string", "format": "uuid", "maxLength": 36, "minLength": 36 }
                },
                {
                    "name": "apiId",
                    "in": "query",
                    "schema": { "type": "string", "format": "uuid", "maxLength": 36, "minLength": 36 }
                }],
                "responses": {
                    "200": { "description": "Success" }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "id": { "type": "string", "format": "uuid", "maxLength": 36, "minLength": 36 }
        }
    }
}



Depending on the size of the referenced entities and the complexity of the OAS3 definition being imported, this could result in the API Gateway consuming more memory than necessary, and producing much larger files when exporting an OAS3 representation of a front-end API from the API Catalog.


Resolution


As of API Manager February 2024 release, API Manager has been updated as follows to address this issue:



  • The employed OAS3 parser now only resolves externally referenced entities, leaving local references unchanged; this can reduce the memory footprint at import-time and decrease the amount of data stored in Cassandra.
  • Local references are now only exploded when necessary; for example, during initialization of runtime parameter validation, or in the API Manager UI, where schema information is displayed or used. This can also help reduce the memory footprint of the API Manager runtime.
  • When exporting an OAS3 API from the API Catalog, the originally imported definition is used as a guide to minimize the number of entities that are exploded, meaning that local references defined in the imported definition will be present in the exported file; this can reduce the size of the exported file considerably.
  • Depending on the structure of the OAS3 API being imported, and the type and prevalence of references ($ref) contained therein, these changes could improve the memory consumption of the API Gateway as well as reduce the size of the file produced when an OAS3 API is exported using the API Catalog.



However, there are some caveats, as outlined further down.


Referenced path item entities



The latest version of OAS3 supported by API Manager (v3.0.2) allows for a path item to reference an external entity. For more information, see OpenAPI Specification, Path Item Object. Because this version of OAS3 does not support locally referenced path items, all such path items will be exploded. This will cause the file exported from the API Catalog to be larger than that imported.



For example, given the following OAS3 definition


{
    "openapi": "3.0.2",
    "info": { "title": "Test", "version": "0.0.1" },
    "servers": [{ "url": "http://localhost:8080" }],
    "paths": {
        "/test": {
            "$ref": "http://localhost:9090/pathItem/test.json"
        }
    }


where http://localhost:9090/pathItem/test.json is resolved to:


{
    "post": {
        "operationId": "test",
        "requestBody": {
            "content": {
                "application/json": {
                    "schema": {
                        "required": [ "firstName", "lastName" ],
                        "type": "object",
                        "properties": {
                            "firstName": { "type": "string" },
                            "lastName": { "type": "string" }
                        }
                    }
                }
            }
        },
        "responses": {
            "201": { "description": "Created" }
        }
    }
}


the following definition will be exported from the API Catalog:


{
    "openapi": "3.0.1",
    "info": { "title": "Test", "version": "0.0.1" },
    "servers": [{ "url": "https://localhost:8065/api" }],
    "paths": {
        "/test": {
            "post": {
                "operationId": "test",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "required": [ "firstName", "lastName" ],
                                "type": "object",
                                "properties": {
                                    "firstName": { "type": "string" },
                                    "lastName": { "type": "string" }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": { "description": "Created" }
                }
            }
        }
    },
    "x-axway": {
        "serviceType": "rest",
        "basePaths": [ "https://localhost:8065" ],
        "expired": false,
        "retirementDate": 0,
        "corsEnabled": true,
        "deprecated": false,
        "availableSDK": {
            "ios-swift": "/discovery/sdk/b93cb899-b80c-4f69-86fe-34df99ffdcb8/ios-swift",
            "android": "/discovery/sdk/b93cb899-b80c-4f69-86fe-34df99ffdcb8/android",
            "nodejs": "/discovery/sdk/b93cb899-b80c-4f69-86fe-34df99ffdcb8/nodejs"
        },
        "apiResourceType": "oas30",
        "id": "b93cb899-b80c-4f69-86fe-34df99ffdcb8",
        "state": "unpublished",
        "accessGrantedDate": 1709503609317
    }
}


Referenced path item parameters



OAS3 allows for parameters that are common to all underlying operations to be defined at the path item itself. For more information, see OpenAPI Specification, Path Item Object. Currently, there is no way to regenerate referenced parameters.


For example, given the following OAS3 definition:


{
    "openapi": "3.0.2",
    "info": { "title": "Test", "version": "0.0.1" },
    "servers": [{ "url": "http://localhost:8080" }],
    "paths": {
        "/test": {
            "parameters": [{ "$ref": "#/components/parameters/dx" }, { "$ref": "#/components/parameters/dy" }],
            "get": {
                "operationId": "test.read",
                "responses": {
                    "200": { "description": "Success" }
                }
            },
            "post": {
                "operationId": "test.create",
                "responses": {
                    "200": { "description": "Success" }
                }
            }
        }
    },
    "components": {
        "parameters": {
            "dx": {
                "name": "dx",
                "in": "query",
                "schema": { "type": "integer", "format": "int32" }
            },
            "dy": {
                "name": "dy",
                "in": "query",
                "schema": { "type": "integer", "format": "int64" }
            }
        }
    }
}



the following definition will be exported from the API Catalog:

{
    "openapi": "3.0.1",
    "info": { "title": "Test", "version": "0.0.1" },
    "servers": [{ "url": "https://localhost:8065/api2" }],
    "paths": {
        "/test": {
            "get": {
                "operationId": "test.read",
                "parameters": [
                    {
                        "name": "dx",
                        "in": "query",
                        "required": false,
                        "schema": { "type": "integer", "format": "int32" }
                    },
                    {
                        "name": "dy",
                        "in": "query",
                        "required": false,
                        "schema": { "type": "integer", "format": "int64" }
                    }
                ],
                "responses": {
                    "200": { "description": "Success" }
                }
            },
            "post": {
                "operationId": "test.create",
                "parameters": [
                    {
                        "name": "dx",
                        "in": "query",
                        "required": false,
                        "schema": { "type": "integer", "format": "int32" }
                    },
                    {
                        "name": "dy",
                        "in": "query",
                        "required": false,
                        "schema": { "type": "integer", "format": "int64" }
                    }
                ],
                "responses": {
                    "200": { "description": "Success" }
                }
            }
        }
    },
    "components": {
        "parameters": {
            "dx": {
                "name": "dx",
                "in": "query",
                "required": false,
                "style": "form",
                "explode": true,
                "schema": { "type": "integer", "format": "int32" }
            },
            "dy": {
                "name": "dy",
                "in": "query",
                "required": false,
                "style": "form",
                "explode": true,
                "schema": { "type": "integer", "format": "int64" }
            }
        }
    },
    "x-axway": {
        "serviceType": "rest",
        "basePaths": [ "https://localhost:8065" ],
        "expired": false,
        "retirementDate": 0,
        "corsEnabled": true,
        "deprecated": false,
        "availableSDK": {
            "ios-swift": "/discovery/sdk/013d75a2-949d-479a-bfed-f6320c66b759/ios-swift",
            "android": "/discovery/sdk/013d75a2-949d-479a-bfed-f6320c66b759/android",
            "nodejs": "/discovery/sdk/013d75a2-949d-479a-bfed-f6320c66b759/nodejs"
        },
        "apiResourceType": "oas30",
        "id": "013d75a2-949d-479a-bfed-f6320c66b759",
        "state": "unpublished",
        "accessGrantedDate": 1709562211413
    }
}



Referenced parameters end up being duplicated for each operation that exists under the defined path item, causing the exported file to be larger than that imported.


Referenced operation responses



OAS3 allows for response entities to be referenced in their entirety. For more information, see OpenAPI Specification, Responses Object. Currently, there is no way to regenerate referenced responses.


For example, given the following OAS3 definition:


{
    "openapi": "3.0.2",
    "info": { "title": "Test", "version": "0.0.1" },
    "servers": [{ "url": "http://localhost:8080" }],
    "paths": {
        "/test": {
            "get": {
                "operationId": "test",
                "responses": {
                    "200": { "$ref": "#/components/responses/success" },
                    "403": { "$ref": "#/components/responses/forbidden" },
                    "404": { "$ref": "#/components/responses/notFound" }
                }
            }
        }
    },
    "components": {
       "responses": {
           "success": { "description": "Success" },
           "forbidden": {
              "description": "Forbidden",
               "content": {
                   "application/json": {
                       "schema": { "$ref": "#/components/schemas/Error" }
                   }
               }
           },
           "notFound": {
              "description": "Not Found",
               "content": {
                  "application/json": {
                      "schema": { "$ref": "#/components/schemas/Error" }
                  }
              }
          }
        },
        "schemas": {
          "Error": {
            "type": "object",
            "properties": {
              "code": { "type": "integer", "format": "int32", "minimum": 0, "maximum": 99 },
              "severity": { "type": "string", "enum": [ "low", "medium", "high", "critical" ] },
              "message": { "type": "string", "maxLength": 1024 }
            }
         }
      }
   }
}



the following definition will be exported from the API Catalog:



{
    "openapi": "3.0.1",
    "info": { "title": "Test", "version": "0.0.1" },
    "servers": [{ "url": "https://localhost:8065/api3" }],
    "paths": {
        "/test": {
            "get": {
                "operationId": "test",
                "responses": {
                    "200": { "description": "Success" },
                    "403": {
                        "description": "Forbidden",
                        "content": {
                            "application/json": {
                                "schema": { "$ref": "#/components/schemas/Error" }
                            }
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "content": {
                            "application/json": {
                                "schema": { "$ref": "#/components/schemas/Error" }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
          "Error": {
            "type": "object",
            "properties": {
              "code": { "type": "integer", "format": "int32", "minimum": 0, "maximum": 99 },
              "severity": { "type": "string", "enum": [ "low", "medium", "high", "critical" ] },
              "message": { "type": "string", "maxLength": 1024 }
            }
          }
        },
        "responses": {
            "success": { "description": "Success" },
            "forbidden": {
                "description": "Forbidden",
                "content": {
                    "application/json": {
                        "schema": { "$ref": "#/components/schemas/Error" }
                    }
                }
            },
            "notFound": {
                "description": "Not Found",
                "content": {
                    "application/json": {
                        "schema": { "$ref": "#/components/schemas/Error" }
                    }
                }
            }
        }
    },
    "x-axway": {
        "serviceType": "rest",
        "basePaths": [ "https://localhost:8065" ],
        "expired": false,
        "retirementDate": 0,
        "corsEnabled": true,
        "deprecated": false,
        "availableSDK": {
            "ios-swift": "/discovery/sdk/c42c1382-b467-4e1f-b71a-9c0bd752b528/ios-swift",
            "android": "/discovery/sdk/c42c1382-b467-4e1f-b71a-9c0bd752b528/android",
            "nodejs": "/discovery/sdk/c42c1382-b467-4e1f-b71a-9c0bd752b528/nodejs"
        },
        "apiResourceType": "oas30",
        "id": "c42c1382-b467-4e1f-b71a-9c0bd752b528",
        "state": "unpublished",
        "accessGrantedDate": 1709563260878
    }
}


Referenced responses end up being duplicated, causing the exported file to be larger than that imported.


For more information on API Manager February 2024 release, see API Gateway and API Manager 7.7 February 2024 Release Notes.