Actions3 2.0 has been released. A new release provides a new module for searching AWS CloudWatch logs with the Insights query. All features are described in the User guide. In this piece I'll focus on technical improvements in the new version.
Actions3 1.0 was using SQLite database as storage for application configuration and views configuration. SQLite was a perfect choice at that time because it allowed me to focus on the application's features, however it has one drawback. SQLite doesn't have a native Java implementation which means a cross platform application must contain libraries for every operating system. In consequence the size of the executable JAR was pretty high - almost 27 megabytes. Also GraalVM compilation doesn't work well with native C libraries.
The experiment with the MVStore didn't end well. The application was crashing often due to corrupted memory. Also MVStore implements an exclusive read lock on a data file - only one application's instance can keep a data file open.
Action3 2.0 comes with new key/value storage implemented in pure Java. Entries are organized as a binary tree. ACID properties are provided with a simple MVCC implementation. Every data change is written to a transaction which is represented as a temporary disk file - a copy of the data file. When a client commits a transaction, a data file is overridden with a transaction file and the current file's version is updated. When a client tries to commit a parallel transaction that operates on an outdated version, an exception is raised and the request is rejected.
Because all writing operations are first done in a temporary file there is no risk of data corruption. Before the final commit write, a backup is created in order to avoid data corruption if the application crashes during a commit phase. The latest valid version will be recovered automatically when the application starts again.
Data storage was implemented with data correctness as the main factor. With big data sets, a performance of an unbalanced binary search tree might be lower than desired. However, that's not the case with Actions3.
Data entries are organized as a binary search tree. An entry's key is formed from
partition key and sort key. The former represents data partition or namespace.
The latter is used to sort entries in the scope of the partition key. In the
single tree or file a client can store entries for different name spaces which
might be beneficial for the application's logic. For example query history module
maintains a list of queries in a separate storage file. To ensure ascending order
of entries, every data entry has an additional entry in partition sort_ts_asc
where a sort key is a time stamp of the query.
Version 2.0 introduces a new architecture of the application. Every type of a view,
such as Logs or Metrics, is implemented in a separate Maven module. The core
application discovers modules and communicates with them through implementation
of the SPI interface.
This makes adding new types of views relatively easy and doesn’t require changes
in the core application. When the application starts it automatically discovers
all implementations of the interface AppViewSupport and uses them when a user
opens a specific view.
I wrote about Java Platform Modules System once and I
explained why I like it. That article mentions a problem with building an
application distribution if an application requires automatic modules. I've hit
this problem in the Actions3 because core dependencies, such as AWS SDK or
JFreeChart, don't provide information about required modules. I've solved that
problem eventually
and I was able to build a distribution package with the custom JRE which was the
primary reason why I needed JPMS.
In Actions3 1.0 users who use the IAM Identity Center credentials had to execute
aws sso login from the command line before opening any view in the application.
From version 2.0 the application is able to detect an outdated security token and
initiate the Device Authorization Grant
flow. The application uses the AWS SDK to generate a device code
and opens a verification URL in the Web browser. Users have to compare a device
code that is presented in the application with a value that is shown in the Web
browser. When they confirm that authentication is legit, the application loads data
with the new token and displays data.
Version 1.x was using the default Swing Look And Feel. It's not perfect but has two important advantages - application looks the same on every OS and it doesn't require additional dependencies. Unfortunately the default LAF doesn't work well on HiDPI monitors. And it became an issue when I started using such a device. In such case Java developer has few options to choose:
Logs module runs a CloudWatch Insights query and displays results in tabular
form. Before execution the parser analyzes submitted query
in order to extract columns, detect data type, format query and rewrite it.
Those were changes introduced in version 2.0. However there is still a lot to improve in the application.
Pursuite for zero dependencies application is ongoing. The AWS SDK dependency is a next candidate for removal. Application uses only small subset of SDK methods and the communication protocol with AWS services is well defined so replacing the AWS SDK with an in-house implementation should be doable with reasonable effort. It should simplify building distribution packages and reduce its size.
Users should be able to define custom actions - procedures with some logic that
can be executed in the sandbox on demand. Such procedure can be defined in some
programming language and perform task or operation that is not supported in the
application.
Since the beginning, to define new or modify existing action user need to write a JSON object which is error-prone and not very convenient. To improve user's experience every view module should provide a wizard that allows creating new action in step-by-step procedure.