plantuml-stdlib / C4-PlantUML

C4-PlantUML combines the benefits of PlantUML and the C4 model for providing a simple way of describing and communicate software architectures
MIT License
6.44k stars 1.1k forks source link

Java OutOfMemoryError with some Sequence diagrams #348

Closed SlavaVedernikov closed 6 months ago

SlavaVedernikov commented 7 months ago

I'm generating C4-PlantUML diagrams as output from my own framework, and it works great in general. thank you.

I'm having issues with some Sequence diagrams though. Java throws an OutOfMemoryError.

image

Here is an example of the Sequence diagram that does not render.

@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Sequence.puml

title Order Api - Api - Order Controller - Create Order - C4 Sequence - Component level

System_Ext(C4InterFlow.SoftwareSystems.ExternalSystem, "External", "")

System_Boundary(OrderApi, "Order Api")

Container_Boundary(OrderApi.Containers.Api, "Api")
    Component(OrderApi.Containers.Api.Components.OrderController, "Order Controller", "", "")
Boundary_End()

Container_Boundary(OrderApi.Containers.Data, "Data")
    Component(OrderApi.Containers.Data.Components.OrderDbContext, "Order Db Context", "", "")
Boundary_End()

Container_Boundary(OrderApi.Containers.Application, "Application")
    Component(OrderApi.Containers.Application.Components.OrderService, "Order Service", "", "")
    Component(OrderApi.Containers.Application.Components.DistributedLockProvider, "Distributed Lock Provider", "", "")
    Component(OrderApi.Containers.Application.Components.UserAuditLogService, "User Audit Log Service", "", "")
Boundary_End()

Container_Boundary(OrderApi.Containers.Integration, "Integration")
    Component(OrderApi.Containers.Integration.Components.InfinaProxy, "Infina Proxy", "", "")
    Component(OrderApi.Containers.Integration.Components.UserAuditLogApiProxy, "User Audit Log Api Proxy", "", "")
Boundary_End()
Boundary_End()

Rel(C4InterFlow.SoftwareSystems.ExternalSystem, OrderApi.Containers.Api.Components.OrderController, "Create Order")
group Create Order
alt request.IsInternalOrder
Rel(OrderApi.Containers.Api.Components.OrderController, OrderApi.Containers.Api.Components.OrderController, "Create Internal Order")
group Create Internal Order
alt request.Side == OrderSide.Buy
Rel(OrderApi.Containers.Api.Components.OrderController, OrderApi.Containers.Api.Components.OrderController, "Calculate Fee Amount")
end
Rel(OrderApi.Containers.Api.Components.OrderController, OrderApi.Containers.Data.Components.OrderDbContext, "Orders Add")
Rel(OrderApi.Containers.Api.Components.OrderController, OrderApi.Containers.Data.Components.OrderDbContext, "Outbox Messages Add")
Rel(OrderApi.Containers.Api.Components.OrderController, OrderApi.Containers.Api.Components.OrderController, "Retun (response)")
end
else 
Rel(OrderApi.Containers.Api.Components.OrderController, OrderApi.Containers.Application.Components.OrderService, "Create Order")
group Create Order
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.DistributedLockProvider, "Try Acquire Lock Async")
alt handle != null
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Map Channel To Infina Programme Name")
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Map Client Id To Infina Platform Name")
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Integration.Components.InfinaProxy, "Create Order")
group Create Order
Rel(OrderApi.Containers.Integration.Components.InfinaProxy, OrderApi.Containers.Integration.Components.InfinaProxy, "Retun (proxyResponse)")
end
alt apiResponse.HasError
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Throw Exception Create Order Failed")
group Throw Exception Create Order Failed
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Get Problem Details Exception By Infina Oms Error")
group Get Problem Details Exception By Infina Oms Error
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Extract Error Code From Infina Oms Error Message")
alt match.Success
alt int.TryParse(errorCodeMatch.Value, out int code)
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Retun (code)")
end
end
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Retun (problem)")
end
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Exception (ProblemDetailsException)")
end
end
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Send Order Created Log To User Audit Log")
group Send Order Created Log To User Audit Log
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Get Order Side")
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Get Order Type")
alt request.Type == OrderType.Limit
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.UserAuditLogService, "Send User Audit Log")
group Send User Audit Log
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Get Async Fallback Policy")
group Get Async Fallback Policy
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Create Outbox Message")
group Create Outbox Message
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Data.Components.OrderDbContext, "Outbox Messages Add")
end
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Retun (fallback)")
end
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Get Async Retry Policy")
group Get Async Retry Policy
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Retun (retryPolicy)")
end
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Get Async Circuit Breaker Policy")
group Get Async Circuit Breaker Policy
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Retun (breaker)")
end
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Integration.Components.UserAuditLogApiProxy, "Create User Audit Log")
group Create User Audit Log
alt responseMessage.IsSuccessStatusCode
Rel(OrderApi.Containers.Integration.Components.UserAuditLogApiProxy, OrderApi.Containers.Integration.Components.UserAuditLogApiProxy, "Retun (proxyResponse)")
end
Rel(OrderApi.Containers.Integration.Components.UserAuditLogApiProxy, OrderApi.Containers.Integration.Components.UserAuditLogApiProxy, "Retun (proxyResponse)")
end
end
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Retun")
end
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.UserAuditLogService, "Send User Audit Log")
group Send User Audit Log
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Get Async Fallback Policy")
group Get Async Fallback Policy
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Create Outbox Message")
group Create Outbox Message
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Data.Components.OrderDbContext, "Outbox Messages Add")
end
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Retun (fallback)")
end
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Get Async Retry Policy")
group Get Async Retry Policy
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Retun (retryPolicy)")
end
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Get Async Circuit Breaker Policy")
group Get Async Circuit Breaker Policy
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Application.Components.UserAuditLogService, "Retun (breaker)")
end
Rel(OrderApi.Containers.Application.Components.UserAuditLogService, OrderApi.Containers.Integration.Components.UserAuditLogApiProxy, "Create User Audit Log")
group Create User Audit Log
alt responseMessage.IsSuccessStatusCode
Rel(OrderApi.Containers.Integration.Components.UserAuditLogApiProxy, OrderApi.Containers.Integration.Components.UserAuditLogApiProxy, "Retun (proxyResponse)")
end
Rel(OrderApi.Containers.Integration.Components.UserAuditLogApiProxy, OrderApi.Containers.Integration.Components.UserAuditLogApiProxy, "Retun (proxyResponse)")
end
end
end
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Retun (response)")
else 
Rel(OrderApi.Containers.Application.Components.OrderService, OrderApi.Containers.Application.Components.OrderService, "Exception (Exception)")
end
end
end
Rel(OrderApi.Containers.Api.Components.OrderController, OrderApi.Containers.Api.Components.OrderController, "Retun (Created)")
end

@enduml

...and here is the same diagram written just in PlantUML, which renders successfully.

@startuml

title Order Api - Api - Order Controller - Delete Order - Sequence - Component level

participant "External" as C4InterFlow.SoftwareSystems.ExternalSystem

box "Order Api" #White

box "Api" #White
    participant "Order Controller" as OrderApi.Containers.Api.Components.OrderController
end box

box "Application" #White
    participant "Order Service" as OrderApi.Containers.Application.Components.OrderService
    participant "Distributed Lock Provider" as OrderApi.Containers.Application.Components.DistributedLockProvider
    participant "User Audit Log Service" as OrderApi.Containers.Application.Components.UserAuditLogService
end box

box "Integration" #White
    participant "Infina Proxy" as OrderApi.Containers.Integration.Components.InfinaProxy
    participant "User Audit Log Api Proxy" as OrderApi.Containers.Integration.Components.UserAuditLogApiProxy
end box

box "Data" #White
    participant "Order Db Context" as OrderApi.Containers.Data.Components.OrderDbContext
end box

end box

C4InterFlow.SoftwareSystems.ExternalSystem -> OrderApi.Containers.Api.Components.OrderController : Delete Order
group Delete Order
OrderApi.Containers.Api.Components.OrderController -> OrderApi.Containers.Application.Components.OrderService : Delete Order
group Delete Order
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.DistributedLockProvider : Try Acquire Lock Async
alt handle != null
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Integration.Components.InfinaProxy : Get Order
group Get Order
alt responseMessage.IsSuccessStatusCode
OrderApi.Containers.Integration.Components.InfinaProxy -> OrderApi.Containers.Integration.Components.InfinaProxy : Retun (proxyResponse)
end
OrderApi.Containers.Integration.Components.InfinaProxy -> OrderApi.Containers.Integration.Components.InfinaProxy : Retun (proxyResponse)
end
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Throw Exception If Get Order Failed While Delete Order
group Throw Exception If Get Order Failed While Delete Order
alt !proxyResponse.HasError
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Retun
end
alt proxyResponse.ProblemDetails.Status == StatusCodes.Status404NotFound
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Exception (ProblemDetailsException)
end
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Get Problem Details Exception By Infina Oms Error
group Get Problem Details Exception By Infina Oms Error
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Extract Error Code From Infina Oms Error Message
alt match.Success
alt int.TryParse(errorCodeMatch.Value, out int code)
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Retun (code)
end
end
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Retun (problem)
end
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Exception (ProblemDetailsException)
end
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Map Client Id To Infina Platform Name
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Map Channel To Infina Programme Name
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Integration.Components.InfinaProxy : Delete Order
group Delete Order
alt responseMessage.IsSuccessStatusCode
OrderApi.Containers.Integration.Components.InfinaProxy -> OrderApi.Containers.Integration.Components.InfinaProxy : Retun (proxyResponse)
end
OrderApi.Containers.Integration.Components.InfinaProxy -> OrderApi.Containers.Integration.Components.InfinaProxy : Retun (proxyResponse)
end
alt response.HasError
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Throw Delete Order Error Exception
group Throw Delete Order Error Exception
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Get Problem Details Exception By Infina Oms Error
group Get Problem Details Exception By Infina Oms Error
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Extract Error Code From Infina Oms Error Message
alt match.Success
alt int.TryParse(errorCodeMatch.Value, out int code)
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Retun (code)
end
end
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Retun (problem)
end
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Exception (ProblemDetailsException)
end
end
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Send Order Cancelled Log To User Audit Log
group Send Order Cancelled Log To User Audit Log
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Get Order Side
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Get Order Type
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.UserAuditLogService : Send User Audit Log
group Send User Audit Log
OrderApi.Containers.Application.Components.UserAuditLogService -> OrderApi.Containers.Application.Components.UserAuditLogService : Get Async Fallback Policy
group Get Async Fallback Policy
OrderApi.Containers.Application.Components.UserAuditLogService -> OrderApi.Containers.Application.Components.UserAuditLogService : Create Outbox Message
group Create Outbox Message
OrderApi.Containers.Application.Components.UserAuditLogService -> OrderApi.Containers.Data.Components.OrderDbContext : Outbox Messages Add
end
OrderApi.Containers.Application.Components.UserAuditLogService -> OrderApi.Containers.Application.Components.UserAuditLogService : Retun (fallback)
end
OrderApi.Containers.Application.Components.UserAuditLogService -> OrderApi.Containers.Application.Components.UserAuditLogService : Get Async Retry Policy
group Get Async Retry Policy
OrderApi.Containers.Application.Components.UserAuditLogService -> OrderApi.Containers.Application.Components.UserAuditLogService : Retun (retryPolicy)
end
OrderApi.Containers.Application.Components.UserAuditLogService -> OrderApi.Containers.Application.Components.UserAuditLogService : Get Async Circuit Breaker Policy
group Get Async Circuit Breaker Policy
OrderApi.Containers.Application.Components.UserAuditLogService -> OrderApi.Containers.Application.Components.UserAuditLogService : Retun (breaker)
end
OrderApi.Containers.Application.Components.UserAuditLogService -> OrderApi.Containers.Integration.Components.UserAuditLogApiProxy : Create User Audit Log
group Create User Audit Log
alt responseMessage.IsSuccessStatusCode
OrderApi.Containers.Integration.Components.UserAuditLogApiProxy -> OrderApi.Containers.Integration.Components.UserAuditLogApiProxy : Retun (proxyResponse)
end
OrderApi.Containers.Integration.Components.UserAuditLogApiProxy -> OrderApi.Containers.Integration.Components.UserAuditLogApiProxy : Retun (proxyResponse)
end
end
end
else 
OrderApi.Containers.Application.Components.OrderService -> OrderApi.Containers.Application.Components.OrderService : Exception (Exception)
end
end
OrderApi.Containers.Api.Components.OrderController -> OrderApi.Containers.Api.Components.OrderController : Retun (Accepted)
end

@enduml

Please let me know if I'm doing something unexpected/wrong etc. ...or maybe it's a bug (?)

kirchsth commented 7 months ago

Hi @SlavaVedernikov

I found the problem. It is !pragma teoz true. I use the !pragma teoz true that the sequence diagram supports multiple boundaries too.

E.g. in your case with the !pragma teoz true (default of C4_Squenece.puml) your components/participants look like below (incl. "Order API" boundary)

Without teoz (or in our case an additional !pragma teoz false) they look like below ("Order API" boundary is lost).

If you don't need the overall boundary then you can simply fix it with an !pragma teoz false line after the include.

@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Sequence.puml
'following fixes your memory problem
!pragma teoz false

Best regards Helmut

PS.: I entered an issue in forum, but I'm not sure if we can get a fix

kirchsth commented 7 months ago

Hi @SlavaVedernikov,

a second alternative could be that you increase the available memory, e.g. start the tool with 8GB like

java  -Xms8192m  -Xmx8192m  -jar plantuml.jar "YOUR_FILE.puml"

but then the build is very slow (I checked it on my machine and the C4-Stdlib version requires ~1min40sec instead of ~5sec if you build it without teoz)

BR Helmut

SlavaVedernikov commented 7 months ago

Hi @kirchsth

Thank you for investigating this. I think I'll stick with !pragma teoz false for now.

Thanks, Slava

Potherca commented 6 months ago

Not sure yet, but we might might want to add "does not always play well with teoz" to the docs "somewhere".

arnaudroques commented 6 months ago

The root cause of this issue has been identified: https://github.com/plantuml/plantuml/pull/1777#issuecomment-2118408574

We will commit the fix in the incoming days. Thanks!

The-Lum commented 6 months ago

Hi all,

FYI: This is now fixed on PlantUML V1.2024.5.

Regards.