Skip to content

Releases: cinchapi/runway

Version 1.10.1

01 Oct 23:45

Choose a tag to compare

  • Fixed a bug that caused the countAny(Class, Criteria, Realms) method to incorrectly count records within only the specified class instead of across the entire class hierarchy.

Version 1.10.0

11 May 18:52

Choose a tag to compare

Deletion Hooks

New deletion hooks are available to ensure automatic referential integrity when records are deleted. These annotations streamline data management by automatically handling dependencies between records.

  • @CascadeDelete: Simplifies deletion of dependent records within the framework. Fields annotated with @CascadeDelete automatically delete their linked records when the containing record is removed. This functionality ensures that related records do not persist after their parent records are deleted, preserving consistency. Deletions occur in a single, atomic transaction, allowing for more efficient data cleanup.

  • @JoinDelete: Automates the deletion of containing records when a linked record is removed. Fields annotated with @JoinDelete trigger the deletion of the containing record if the linked record is deleted. This is the reverse of @CascadeDelete, as it removes all parent or container records that depend on the existence of linked records, thereby ensuring referential integrity. The operation is performed atomically.

  • @CaptureDelete: Facilitates automatic reference removal for cases where a linked record is deleted but the containing record should remain intact. When a record is deleted, fields annotated with @CaptureDelete are automatically set to null or removed from the containing record's collection. This allows for more flexible data management, maintaining integrity without deleting the containing record.

Save Lifecycle Hooks

Runway now provides comprehensive options for injecting logic into the save routine and responding to save events, enabling more flexible and reactive data management patterns.

  • Breaking Change: The Record#save method has been made final to ensure consistent behavior across all save operations, including bulk saves. Previously, overridden save methods were not called during Runway's bulk save operations, leading to inconsistent behavior. Applications should migrate any custom save logic to either the beforeSave hook or save listeners.

  • beforeSave Hook: Added a protected beforeSave method to the Record class that is automatically called before a record is saved to the database. This hook allows records to update their state or perform validation immediately before persistence:

    • Executes within the same transaction as the save operation, ensuring atomicity
    • Can modify record fields, with changes included in the save operation
    • Allows for custom validation beyond what annotations provide
    • Exceptions thrown from beforeSave abort the save operation and roll back the transaction
    • Works consistently with both individual and bulk save operations
  • Save Listeners: Added support for registering save listeners that allow applications to be notified when records are successfully saved. This feature enables reactive workflows and improved integration with external systems:

    • Save Notification: Using the onSave method of the Runway.builder(), applications can register a listener that will be called whenever a record is successfully saved. The listener is called asynchronously after the save transaction is committed, ensuring that notifications only occur for successful operations.
    • Efficient Processing: Save notifications are processed in a dedicated background thread, allowing the main application to continue without waiting for notification processing to complete.
    • Error Tolerance: Exceptions thrown by save listeners are silently swallowed, ensuring that listener errors don't affect the application's core functionality.
  • overrideSave Hook: Added a protected overrideSave method to the Record class that allows completely bypassing the standard save routine:

    • Returns a Supplier<Boolean> that determines the result of the save operation without database interaction
    • When non-null, the normal persistence mechanism is skipped entirely
    • Useful for creating in-memory only records or implementing custom persistence logic
    • Works consistently with both individual and bulk save operations
New Functionality and Enhancements
  • Added @Computed and @Derived annotations that can be applied to methods to mark them as returning computed and derived properties, respectively. These annotations are meant to be used in lieu of the #computed() and #derived() methods, which are now deprecated
  • Introduced a new Record.set(Map<String, Object> data) method that allows for bulk updating of fields within a record.
  • added Runway.ping() to provide an interface to Concourse's new ping healthcheck, introduced in 0.11.10.
Improvements
  • Improved Runway's bulk loading functionality to ensure that the same object reference is used for a linked Record that exists as a value in multiple records. Previously, in a single bulk load operation, Runway would create a new Java object for EVERY loaded reference, regardless of whether that referenced object was already encountered earlier in the load, which created unnecessary heap bloat. This optimization reduces memory usage and ensures object identity is maintained across references to the same record within a single load operation.
  • Optimized computed value generation to ensure values are only computed once per map operation. Previously, when filtering null values during serialization, computed values were unnecessarily generated twice - once during the null check and again when adding to the result map. This improvement caches computed values within each map operation while still ensuring fresh values are generated for each new operation.
  • Enhanced the save functionality to detect when an existing Record has been modified and only attempt to write to the database if its required to reflect new state. Previously, whenever a Record was saved, Runway always attempted to write to the database, even if doing so was a no-op. This optimization eliminates unnecessary database operations and improves performance. This is especially true for Records that link to other Records since Runway's save functionality cascades and automatically save's any referenced Records to maintain referential integrity. Now, when a save is cascaded, those referenced Records will only perform database writes if they have indeed been modified.
Bug Fixes
  • Fixed a regression that casued a NullPointerException to be thrown when a null intrinsic, derived or computed value was encountered while performing local condition evaluation.
  • Fixed a few bugs that caused @Required, @Unique and @ValidatedBy constraints to behave unexpectedly in certain scenarios:
    • For a field containing a Sequence value, @ValidatedBy was applied to the entire Sequence as a whole, instead of to each item in the Sequence indivudally.
    • For a field containing a Sequence value, @Unique was checked for the entire Sequence as a whole, instead of for each item in the Sequence indivudally.
    • For a field containing a Sequence value, @Required was not properly enforced in cases when the Sequence was empty.
  • Fixed a bug that made it possible for a field containing a Sequence of DeferredReference objects, to have items in that sequence erroneously removed if those items were not loaded using DeferredReference.get() before the housing Record was saved.
  • Fixed a bug that caused a NoSuchElementException to be thrown instead of an IllegalStateException when attempting to load an non-existing Record.
  • Fixed a bug that caused record deletion via deleteOnSave to not persist if the deleted Record was saved using Runway.save(Record...) bulk save functionality.

Version 1.9.4

22 Jul 23:20

Choose a tag to compare

  • Fixed a bug that occurred when using pre-select to load a Record containing a reference field whose declared type is the parent class of a descendant class with additionally defined fields and the stored value for that field is an instance of that descendant class. In this case, the pre-select logic did not load data for the descendant defined fields, which resulted in unexpected NullPointerException regressions or an overall inability to load those Records if the descendant defined field was annotated as Required.
  • Improved the efficiency of local condition evaluation by removing unnecessary data copying.
  • Addressed performance regressions that have been observed when performing pagination alongside a locally resolvable filter or condition whose matches are sparsely distributed among the unfiltered results. The pagination logic still incrementally loads possible matches (instead of all-at-once), but uses additional logic to dynamically adjust the number of possible matches loaded based on whether the previous batch contained any matches.

Version 1.9.3

04 Jul 23:53

Choose a tag to compare

  • For instances of Concourse Server at version 0.11.3) or greater, we improved overall read performance by pre-selecting data for linked Records, whenever possible. Previously, if a Record contained an attribute whose type was another Record, Runway would eagerly load the data for that reference in a separate database call. So, if Runway needed to process a read of many Records with references to other Records, performance was poor because there were too many database round trips required. Now, Runway will detect when a Record has references to other Records and will pre-select the data for those references while selecting the data for the parent Record if it is possible to do so. This greatly reduces the number of database round trips which drastically improves performance by up to 89.7%.
    • This improvement is automatically enabled whenever Runway is connected to a Concourse deployment at version [0.11.3+]. If necessary, it is possible to disable the functionality when building a Runway instance by invoking the disablePreSelectLinkedRecords() method.
  • Added a new Runway.properties() method that exposes an interface to get metadata and other information about a Runway instance. This interface can be used to query whether a Runway is capable and configured to take advantage of pre-selection.
  • Improved the performance of Runway commands that perform pagination when a filter or a condition that must be resolved locally (e.g., because it references derived or computed keys not in the database) is provided. Previously, in these cases, Runway would load all possible records before applying the filter or condition and lastly performing pagination. Now, Runway incrementally loads possible matching records and applies the filter or condition on the fly until the requested Page has been filled.
  • Removed the com.cinchapi.runway.util.Paging class that was copied from the concourse-server project since it is no longer used for internal pagination logic.
  • Removed unnecessary random result set access when lazily instantiating the Set of records that match a Runway operation.
  • Optimized load performance by
    • using more intelligent logic to scaffold a Record instance and
    • performing static analysis and caching immutable metadata for Record types that was previously computed during each load.

Version 1.9.2

19 Mar 00:41

Choose a tag to compare

  • Upgraded the underlying Concourse client dependency to version 0.11.2, which means that Runway now supports specifying a CCL function statement as an operation key or an operation value if it is connected to a Concourse Server that is version 0.11.0+.

Version 1.9.1

21 Feb 00:45

Choose a tag to compare

  • Fixed a bug that randomly causes a spurious error to be thrown indicating that a Record attribute doesn't exist in the database when an attempt is made to access it.

Version 1.9.0

14 Aug 18:58

Choose a tag to compare

  • Added support for multi-field Unique value constraints. When applying the Unique constraint to a Record field, you can now provide a name parameter (e.g. @Unique(name = "identity")). If multiple Unique annotated fields have the same name, Runway will enforce uniqueness among the combination of values for all those fields across all Records in the same class. If a Unique annotated field is a Sequence, Runway will consider uniqueness to be violated if and only if any items in the sequence are shared and all the other fields in the same uniqueness group are also considered shared.
  • Added Realms to virtually segregate records within the same environment into distinct groups. A Record can be dynamically added to or removed from a realm (use Record#addRealm and Record#removeRealm to manage). Runway provides overloaded read methods that accept a Realms parameter to specify the realms from which data can be read. If a Record exists in at least one of the specified Realms, it will be read.
    • By default, all Records exist in ALL realms, so this feature is backwards compatible.
    • By default, read methods consider data from ANY realm, so this feature is backwards compatible.
  • Fixed a bug where the Required annotation was not enforced when loading data from the database. If a record was modified outside of Runway such that a required field was nullified, Runway would previously load the record without enforcing the constraint. This caused applications to encounter some unexpected NullPointerExceptions.

Version 1.8.1

20 Apr 21:12

Choose a tag to compare

  • Fixed a bug that allowed for dynamically seting an intrinsic attribute of a Record with a value of an invalid type. In this scenario, Runway should have thrown an error, but it didn't. While the value with the invalid type was not persisted when saving the Record, it was return on intermediate reads of the Record.

Version 1.8.0

12 Feb 12:54

Choose a tag to compare

  • Improved validation exception messages by including the class name of the Record that fails to validate.
  • Added a onLoadFailure hook to the Runway.builder that can be used to get insight and perform processing on errors that occur when loading records from the database. Depending on the error, load failures can be fatal (e.g. the entire load operation fails). The onLoadFailure hook does not change this, but it does ensure that fatal errors can be caught and inspected. By default, Runway uses a non-operational onLoadFailure hook. The hook can be customized by providing a TriConsumer accepting three inputs: the record's Class and id and the Throwable that represents the error.
  • Fixed an issue that occurred when setting a value to null and that value not being removed from the database.

Version 1.7.0

01 Jan 21:35

Choose a tag to compare

  • Fixed a bug that caused Runway to exhibit poor performance when using the withCache option.
  • Fixed bugs that caused Runway's data caching to exhibit inconsistent behaviour where stale data could be added to the cache.
  • Added a Runway#builder option to specify a readStrategy. Runway's read strategy determines how Runway reads data from Concourse.
    • The BULK strategy uses Concourse's select method to pull in all the data for all the records that match a read at the same time.
    • The STREAM option uses Concourse's find method to find the ids of all the records that match a read in order to stream the data for those records on-the-fly when needed.
    • The AUTO option contextually uses the BULK or STREAM option on a read-by-read basis (usually depending on which option will return results faster).
      By default, Runway uses the AUTO strategy unless a cache is provided, in which case, the STREAM option is used by default since data streaming is more cache-friendly and is consistent with the way record caching previously worked in previous versions of Runway.
  • Deprecated the recordsPerSelectBufferSize option in the Runway#builder in favor of the streamingReadBufferSize option which has the same effect.