What are the reasons for this? It causes massive amounts of work for us, because we have been using Bandana a lot (as well as Active Objects and PluginSettings).
PluginSettings are mentioned as an alternative. Unfortunately, they (a) lack a function like getKeys(bandanaContext), (b) we need to serialize ourselves because they only support a very limited amount of types, and (c) they have limits for key and value length: PluginSettings (Shared Application Access Layer API 3.0.0 API) – are those limitations still correct? Are there plans to change anything about them?
Additional question: I read somewhere that Bandana and PluginSettings access the very same data in storage. I.e. data written by Bandana can be accessed by PluginSettings and vice versa. Is that correct? If so, can we rely on that in the future? Or in other words: Can we safely switch from Bandana APIs to PluginSettings APIs without any data migration and still have all data as before?
Unfortunately, Bandana is by design, not performant or scalable and hurts the stability of large instances.
I will look into whether adding such an API is viable and get back to you.
That is correct, it only supports String, List<String>, Properties and Map<String, String>. Although, the current implementation does not correctly enforce the generic types, which will be rectified in 10.0. You are indeed free to leverage any other serialization mechanism you deem suitable.
Those limitations are correct and similar to those of Bandana.
PluginSettingsFactory/PluginSettings is currently backed by Bandana and so this is correct currently. This will no longer be the case from 10.0; however, we will copy across data which is compatible (that is values of types String, List<String>, Properties and Map<String, String>). Values of other types will be read-only from Bandana for the life of 10.0 before its permanent removal.
We won’t be adding a method to iterate keys in PluginSettings instances. Given such instances are not scoped to a plugin and keys from multiple plugins will be present, we don’t advise taking this approach. Plugins should instead track which keys they are interested in by another means or perhaps make use of the Map value type. If none of this is suitable for your plugin, you may want to consider defining your own table using ActiveObjects.
@Kusal This should really not be the reason for not introducing it. Plugins have dealt with this since the introduction of Bandana and I’m not aware of major problems caused by it. In fact, the persistence layer is often the only / least cumbersome way for plugins from separate vendors to integrate with each other, and taking away such APIs leaves us with less flexibility to provide value to our customers.
We are currently not using the getKeys API ourselves but we did so in the past. The reason was that we wanted to keep individual entries to avoid locking scenarios when multiple threads needed to work with separate entries, but still needed a management interface etc. for listing all entries where the key matched a prefix (our plugin key). This would have been much more fragile when putting all entries into a Map.
In addition to that, PluginSettings enforce a maximum length for the values (at least in the SAL docs) that does not exist with BandanaManager, which will require some vendors to split their “composite” entries into several ones.
At Atlas Camp it has been brought up that customers should switch to recent (LTS) versions more quickly and that 3rd party apps lacking compatibility are often a blocker for this. Forcing us vendors into data migrations because APIs are being removed is one of the reasons why making apps compatible takes more time. Just saying …
@Kusal I second @jens remarks with regard to making sure that there are suitable replacements for the current Bandana APIs.
Atlassian is making a habit out of combining deprecations of APIs with restricting access to alternatives and/or not replacing the same functionality through different APIs.
This means that for vendors, deprecating an API not only means refactoring code to use alternatives, but also refactoring logic and sometimes even UX/UI as it is no longer possible to recreate the same behaviour.
Even if removing the feature of getKeys is deliberate, it should be separated from the deprecation of BandanaManager to allow vendors to re-evaluate the impact of such deprecation.
First, let me say that I really appreciate the recent focus on security and platform stability on DC products. But…
I would argue that the API without getKeys is of very limited value. We have been using Bandana as key value store for many years for our apps, partly because we were in other instances disadvised by Atlassian to use ActiveObjects. I don’t have any sources right now, but I remember quite vividly how the future of ActiveObjects was very uncertain some time ago.
We have dynamic keys in some of our apps, so we don’t always know all the keys. Without a getKeys functionality, this API leaves us with two options:
a. Separately storing a set of keys and keeping that in sync with the actual objects
b. Saving all entities of a certain type as a map under one single key
a. Is very bad because if the keys and runs out of sync for some reason (or is lost for another), we have no way of knowing what’s in the store.
b. Is very bad because we’ll always have to serialize and deserialize all objects of a given type. Plus, string values have a character limit.
I don’t believe it will be in platform stability’s favor if we went with 2, although this is what you seem to suggest with “perhaps make use of the Map value type.”
So probably you would recommend to use ActiveObjects instead. But key value has always worked great for us and we don’t want to go and switch everything to an SQL/ORM approach now. Neither do we want to “abuse” ActiveObjects as a key value store where we have a column of type string and save stringified objects there.
Given such instances are not scoped to a plugin and keys from multiple plugins will be present, we don’t advise taking this approach.
Not sure why you wouldn’t scope something that is called PluginSettings by plugin under the hood. Instead, we should use a key value store without knowing what keys we stored? It makes no sense to me
I have another question. We do have customers who have quite long bandana entries. Some of them are longer than 99,000 chars. What will happen to them then? If we switch to SAL PluginSettinigs, will we be able to read these entries at all? You wrote
however, we will copy across data which is compatible (that is values of types String , List<String> , Properties and Map<String, String> ).
will everything that doesn’t fit be cut off and copied over or how do you want to do it?
Have you verified this? Bandana doesn’t support values longer than 100,000 characters as far as I’m aware.
Anything that doesn’t conform will not be migrated automatically. However, it will remain accessible via Bandana (read-only) and you should migrate the data to a suitable alternative manually.
Partially migrated data doesn’t sound fun to me. If the customers upgrade to Confluence 10, you will split our bandana data. Then our app is upgraded and the upgrade task runs for the first time. We then have to search for our data in two different managers. I don’t want to imagine what will happen if such an upgrade task should fail.
Please think about migrating ALL data and do the length checks on writing afterwards. In that case the data can be migrated on the fly.
We don’t intend to delete any data from Bandana, we will simply copy across compatible data. I will update the developer notes to make this clearer, apologies for the confusion.
So you may choose to keep reading data from Bandana and migrate the data using a technique and destination of your choosing. The data will be available to read for the life of 10.x.
We’re looking into providing a getKeys API under a Confluence-specific service API which extends the SAL API. Updating the SAL API itself is problematic as not all products may be able to implement the added API in a performant way.
We’ll likely finalise the decision here within the next month.
Another question here: we need to search for spaces keys (context keys) that have the given bandana key. Bandana was not providing that in the past, that’s why we did that over HibernateContentQueryFactory with
Query query = entityManager.createQuery(
" select cbr.context " +
" from ConfluenceBandanaRecord cbr " +
" where cbr.key = :key " +
" order by cbr.id asc "
);
We needed that for finding all the spaces with a specific setup. Are the any plans to prohibit that direct access to the Bandana tables?
The com.atlassian.confluence.persistence.JpaQueryFactory will remain accessible for creating queries.
Although the com.atlassian.confluence.api.service.content.SpacePropertyService is probably more suitable for your use case. Internally, we will be gradually migrating Space properties currently stored in the Bandana table to this service too.
Please also note that the Bandana table will stop accepting writes after all compatible data is migrated to the new PluginSettings table in 10.0, with the intention that the Bandana table will be deleted in a future release.