Saturday, August 04, 2012

Multiple Concurrent Versions

Multiple Concurrent Versions is the term I use to describe a practice for deploying multiple versions of a web app at the same time. Technically, the concept applies to any kind of deployment bundle, but Java web apps make a good example.

Here are the basic precepts of the practice:
  1. We assume that the thing being deployed, the deployment bundle, is represented as files and folders.
  2. Physically moving the deployment bundle to the deployment target (the runtime environment) is a distinct step from activating a particular version.
  3. The deployment target will, at any given time, contain multiple versions of the same deployment bundle: 
    • old versions that used to be in production 
    • old versions that never made it to production 
    • the current production version 
    • future versions that are very close to production ready
  4. Multiple versions are concurrently deployed and accessible at the same time via version specific urls:

    http://v8.mycompany.com/myapp
    http://v9.mycompany.com/myapp


    The current or default version can be accessed via a version independent url:

    http://mycompany.com/myapp
  5. Activation is the process of marking a particular version as the default version. 
    • Changing the default version from an older to a newer version is called a rollout. 
    • Changing the default version from a newer to an older version is called a rollback.
  6. Each deployment bundle version should have a cryptographic identity (i.e. sha).
  7. Concurrent versions should be isolated. That is, a bug in version 8 shouldn't be able to kill version 7. 

Jar Hell

I recently was called in to help troubleshoot an application. It was a java web app: a war. When I looked inside the WEB-INF/lib directory, here is what I saw:
  1. activation-1.1.jar
  2. asm-3.1.jar
  3. axiom-api-1.2.12.jar
  4. axiom-dom-1.2.12.jar
  5. axiom-impl-1.2.12.jar
  6. axis-1.1.jar
  7. axis2-adb-1.6.1.jar
  8. axis2-codegen-1.6.1.jar
  9. axis2-kernel-1.6.1.jar
  10. axis2-mtompolicy-1.6.1.jar
  11. axis2-transport-http-1.6.1.jar
  12. axis2-transport-local-1.6.1.jar
  13. axis2-xmlbeans-1.6.1.jar
  14. commons-beanutils-1.7.0.jar
  15. commons-beanutils-core-1.7.0.jar
  16. commons-codec-1.3.jar
  17. commons-collections-3.2.jar
  18. commons-configuration-1.2.jar
  19. commons-digester-1.7.jar
  20. commons-discovery-0.2.jar
  21. commons-fileupload-1.2.jar
  22. commons-httpclient-3.0.1.jar
  23. commons-lang-2.1.jar
  24. commons-logging-1.0.4.jar
  25. commons-logging-api-1.0.4.jar
  26. dac-client.jar
  27. dealer-locator-pojos.jar
  28. DealerLocatorWebServices-client-1.1.4.jar
  29. dom4j-1.4.jar
  30. ehcache-core-2.3.0.jar
  31. ehcache-web-2.0.3.jar
  32. ejb-api-3.0.jar
  33. httpcore-4.0.jar
  34. isorelax-20020414.jar
  35. jackson-core-asl-1.7.1.jar
  36. jackson-jaxrs-1.7.1.jar
  37. jackson-mapper-asl-1.7.1.jar
  38. jackson-xc-1.7.1.jar
  39. javax.ejb_3.0.1.jar
  40. javax.jms.jar
  41. jaxb-api-2.2.2.jar
  42. jaxb-impl-2.2.3-1.jar
  43. jaxen-1.1.1.jar
  44. jaxrpc.jar
  45. jersey-core-1.6.jar
  46. jersey-json-1.6.jar
  47. jersey-server-1.6.jar
  48. jettison-1.1.jar
  49. log4j-1.2.8.jar
  50. mex-1.6.1-impl.jar
  51. msv-20020414.jar
  52. neethi-3.0.1.jar
  53. ojdbc6.jar
  54. opensaml-2.2.3.jar
  55. pagination-lib.jar
  56. quartz-1.6.6.jar
  57. quartz-1.7.2.jar
  58. rampart-core-1.6.1.jar
  59. rampart-policy-1.6.1.jar
  60. rampart-trust-1.6.1.jar
  61. relaxngDatatype-20020414.jar
  62. saxpath-1.0-FCS.jar
  63. service-pojos.jar
  64. slf4j-api-1.5.11.jar
  65. slf4j-jdk14-1.6.1.jar
  66. stax-api-1.0-2.jar
  67. stax-api-1.0.1.jar
  68. tbgv2util.jar
  69. TMSFramework-2.2.46.jar
  70. TMSIntegrationFramework-0.0.216.jar
  71. TMSMessagingFramework-0.0.11.jar
  72. TMSUserMgmtFramework-1.0.108.154.jar
  73. warranty-maintainence-pojos.jar
  74. wiztools-commons-lib-0.2.0.jar
  75. woden-api-1.0M9.jar
  76. woden-impl-dom-1.0M9.jar
  77. wsdl4j-1.6.2.jar
  78. wss4j-1.5.12.jar
  79. xalan-2.7.0.jar
  80. xercesImpl-2.2.1.jar
  81. xml-apis-1.0.b2.jar
  82. xmlbeans-2.2.0.jar
  83. XmlSchema-1.4.7.jar
  84. xmlsec-1.4.5.jar
  85. xmltooling-1.2.0.jar
I, myself, have never created an application that uses 85 jars.

Making things worse, the app was running on a heavy weight app server (Web Logic) that added many more classes to the classpath, classes that do not show up in the above list. These included the entire stack of J2EE classes. Plus other classes that WebLogic thinks you might need.

Problem: Any app with this many classes in the classpath will surely have some accidental duplicate classes, possibly even multiple versions of the same class. This can be the source of very hard to track bugs.

This was, in fact, the source of the bugs in this particular application.

Moving forward, I would recommend the following:
  1. Add libraries and jars to your app very judiciously.
  2. Be careful of build tools that might recursively suck in unneeded library dependencies.
  3. Beware of any 3rd party libraries that require a whole bunch of other 3rd party libraries to work.
  4. When a 3rd party library comes with 10 other 3rd party libraries that it depends on, those other libraries might be optional libraries and not needed for your app.
  5. Avoid app servers that litter the classpath with unneeded libraries. 
  6. Be especially cautious of putting classes in your app servers global (cross-web-app) lib folder.
  7. This entire problem starts to goes away as we move toward a more micro-service style architecture.