Releases: cinchapi/runway
Version 1.10.1
Version 1.10.0
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@CascadeDeleteautomatically 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@JoinDeletetrigger 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@CaptureDeleteare automatically set tonullor 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#savemethod has been madefinalto ensure consistent behavior across all save operations, including bulk saves. Previously, overriddensavemethods were not called during Runway's bulk save operations, leading to inconsistent behavior. Applications should migrate any custom save logic to either thebeforeSavehook or save listeners. -
beforeSaveHook: Added a protectedbeforeSavemethod to theRecordclass 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
beforeSaveabort 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
onSavemethod of theRunway.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.
- Save Notification: Using the
-
overrideSaveHook: Added a protectedoverrideSavemethod to theRecordclass 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
- Returns a
New Functionality and Enhancements
- Added
@Computedand@Derivedannotations that can be applied to methods to mark them as returningcomputedandderivedproperties, 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 in0.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
NullPointerExceptionto be thrown when anullintrinsic,derivedorcomputedvalue was encountered while performing localconditionevaluation. - Fixed a few bugs that caused
@Required,@Uniqueand@ValidatedByconstraints to behave unexpectedly in certain scenarios:- For a field containing a Sequence value,
@ValidatedBywas applied to the entire Sequence as a whole, instead of to each item in the Sequence indivudally. - For a field containing a Sequence value,
@Uniquewas checked for the entire Sequence as a whole, instead of for each item in the Sequence indivudally. - For a field containing a Sequence value,
@Requiredwas not properly enforced in cases when the Sequence was empty.
- For a field containing a Sequence value,
- Fixed a bug that made it possible for a field containing a Sequence of
DeferredReferenceobjects, to have items in that sequence erroneously removed if those items were not loaded usingDeferredReference.get()before the housing Record was saved. - Fixed a bug that caused a
NoSuchElementExceptionto be thrown instead of anIllegalStateExceptionwhen attempting toloadan non-existingRecord. - Fixed a bug that caused record deletion via
deleteOnSaveto not persist if the deleted Record was saved usingRunway.save(Record...)bulk save functionality.
Version 1.9.4
- 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
NullPointerExceptionregressions or an overall inability to load those Records if the descendant defined field was annotated asRequired. - Improved the efficiency of local
conditionevaluation by removing unnecessary data copying. - Addressed performance regressions that have been observed when performing pagination alongside a locally resolvable
filterorconditionwhose 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
- 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 aRecordcontained an attribute whose type was anotherRecord, 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 aRecordhas references to other Records and will pre-select the data for those references while selecting the data for the parentRecordif it is possible to do so. This greatly reduces the number of database round trips which drastically improves performance by up to89.7%.- This improvement is automatically enabled whenever
Runwayis connected to a Concourse deployment at version [0.11.3+]. If necessary, it is possible to disable the functionality when building aRunwayinstance by invoking thedisablePreSelectLinkedRecords()method.
- This improvement is automatically enabled whenever
- Added a new
Runway.properties()method that exposes an interface to get metadata and other information about aRunwayinstance. This interface can be used to query whether aRunwayis capable and configured to take advantage of pre-selection. - Improved the performance of
Runwaycommands that perform pagination when afilteror aconditionthat must be resolved locally (e.g., because it references derived or computed keys not in the database) is provided. Previously, in these cases,Runwaywould load all possible records before applying thefilterorconditionand lastly performing pagination. Now,Runwayincrementally loads possible matching records and applies thefilterorconditionon the fly until the requestedPagehas been filled. - Removed the
com.cinchapi.runway.util.Pagingclass that was copied from theconcourse-serverproject 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
Runwayoperation. - Optimized load performance by
- using more intelligent logic to scaffold a
Recordinstance and - performing static analysis and caching immutable metadata for
Recordtypes that was previously computed during each load.
- using more intelligent logic to scaffold a
Version 1.9.2
- Upgraded the underlying
Concourseclient dependency to version0.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 version0.11.0+.
Version 1.9.1
- 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
- Added support for multi-field
Uniquevalue constraints. When applying theUniqueconstraint to aRecordfield, you can now provide anameparameter (e.g.@Unique(name = "identity")). If multipleUniqueannotated fields have the samename, Runway will enforce uniqueness among the combination of values for all those fields across allRecordsin the same class. If aUniqueannotated field is aSequence, 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
Realmsto virtually segregate records within the same environment into distinct groups. ARecordcan be dynamically added to or removed from arealm(useRecord#addRealmandRecord#removeRealmto manage). Runway provides overloaded read methods that accept aRealmsparameter to specify the realms from which data can be read. If a Record exists in at least one of the specifiedRealms, 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
Requiredannotation 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 unexpectedNullPointerExceptions.
Version 1.8.1
- Fixed a bug that allowed for dynamically
seting an intrinsic attribute of aRecordwith 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
- Improved validation exception messages by including the class name of the Record that fails to validate.
- Added a
onLoadFailurehook to theRunway.builderthat 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). TheonLoadFailurehook does not change this, but it does ensure that fatal errors can be caught and inspected. By default, Runway uses a non-operationalonLoadFailurehook. The hook can be customized by providing aTriConsumeraccepting three inputs: the record'sClassandidand theThrowablethat represents the error. - Fixed an issue that occurred when setting a value to
nulland that value not being removed from the database.
Version 1.7.0
- Fixed a bug that caused
Runwayto exhibit poor performance when using thewithCacheoption. - Fixed bugs that caused Runway's data caching to exhibit inconsistent behaviour where stale data could be added to the cache.
- Added a
Runway#builderoption to specify areadStrategy. Runway's read strategy determines how Runway reads data from Concourse.- The
BULKstrategy uses Concourse'sselectmethod to pull in all the data for all the records that match a read at the same time. - The
STREAMoption uses Concourse'sfindmethod 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
AUTOoption contextually uses theBULKorSTREAMoption on a read-by-read basis (usually depending on which option will return results faster).
By default, Runway uses theAUTOstrategy unless acacheis provided, in which case, theSTREAMoption 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.
- The
- Deprecated the
recordsPerSelectBufferSizeoption in theRunway#builderin favor of thestreamingReadBufferSizeoption which has the same effect.