-
Notifications
You must be signed in to change notification settings - Fork 1.2k
chore(core): add preferences to /settings
#5621
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
@@ -729,7 +733,7 @@ default boolean isValidateSampleByFillType() { | |||
*/ | |||
boolean mangleTableDirNames(); | |||
|
|||
default void populateSettings(CharSequenceObjHashMap<CharSequence> settings) { | |||
default void populateSettings(Utf8StringSink sink) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we please have an interface here, such as Utf8Sink rather than a specific class. Also "populateSettings" is ambiguous name:
- lets have JavaDoc here
- lets rename the method, using terminology that makes "direction" unambiguous (are we populating config from sink or sink from config?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renamed method in 1eb812a
private int state = STATE_START; | ||
|
||
public PreferencesParser(CairoConfiguration configuration, CharSequenceObjHashMap<CharSequence> parserMap) { | ||
super(16384, configuration.getPreferencesParserCacheSizeLimit(), configuration.getPreferencesStringPoolCapacity()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if 2 of 3 settings are taken from config, why the initial cache size is hardcoded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the thinking here was that instead of adding yet another config property (which will be barely used, if ever) we can just start with something sensible, and it will expand if it has to.
|
||
@Override | ||
public void close() throws IOException { | ||
sink.close(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have QuietCloseable and IOException is usually not thrown
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in 7a56017
preferencesStore.save(transientState.sink, mode, version); | ||
sendOk(); | ||
} catch (JsonException | CairoError e) { | ||
LOG.error().$("error while saving config").$((Throwable) e).$(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: terminology + formatting "could not" or "cannot" - for consistency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in 013292a
preferencesStore.save(transientState.sink, mode, version); | ||
sendOk(); | ||
} catch (JsonException | CairoError e) { | ||
LOG.error().$("error while saving config").$((Throwable) e).$(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
erroneous behaviour is not tested, also missing input fragmentation test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -59,6 +59,10 @@ default ObjList<String> getContextPathImport() { | |||
return new ObjList<>("/imp"); | |||
} | |||
|
|||
default ObjList<String> getContextPathPreferences() { | |||
return new ObjList<>("/preferences"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is also quite funky to have "preferences" and "settings", those are synonyms. More standard approach would be to have single URL /settings. To save settings we would send POST /settings and to read GET /settings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/preferences
has been removed.
/settings
handles GET, POST and PUT.
PUT: overwrite mode, current preferences gets replaced with the one sent in the request
POST: merge mode, current preferences gets merged with the one sent in the request
GET: returns settings which consists of 2 parts, config which comes from server.conf and preferences
import io.questdb.std.str.AbstractCharSequence; | ||
import io.questdb.std.str.DirectUtf8Sink; | ||
|
||
public abstract class AbstractJsonParser implements JsonParser, Mutable, QuietCloseable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we have abstract class? Preferences parser is the only descendant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ent uses this class too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a comment has been added to this class that it should not be removed
@@ -964,8 +964,7 @@ private ExecutionModel parseCreateMatView( | |||
} | |||
} | |||
|
|||
final String matViewSql = Chars.toString(lexer.getContent(), startOfQuery, endOfQuery); | |||
tableOpBuilder.setSelectText(matViewSql, startOfQuery); | |||
tableOpBuilder.setSelectText(lexer.getContent().subSequence(startOfQuery, endOfQuery), startOfQuery); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a lot less explicit that toString and thus will be error prone. tableOp is a detachable object, it cannot depend on mutable objects. subSequence() returns potentially mutable interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed this back in a57460a
assertEquals("\"preferences\":{\"key1\":\"value1\",\"key2\":\"value2\",\"key3\":\"value3\"}", sink); | ||
} | ||
} catch (Throwable th) { | ||
errors.put(threadIndex, th); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reader and writer threads will be overwriting each other errors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in afd5fc4
} | ||
} | ||
|
||
public synchronized void populateSettings(Utf8StringSink sink) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
svae/populateSettings are not obviously symmetrical method names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
they are not meant to be symmetrical.
for save()
the symmetrical method is load()
.
/settings
[PR Coverage check]😍 pass : 442 / 456 (96.93%) file detail
|
Functionality to save key-value pairs (currently referred to as preferences) on the server into a file via a POST or PUT request sent to the
/settings
endpoint.The saved preferences returned back to the client in the response of a GET request sent to the
/settings
endpoint.server.conf
and preferencesPreferences are sent/received in json format, and versioned.
When preferences are updated, the version on which the changes are based has to be sent to the server.
If the base version is not matching with the current version, meaning another update was already processed, the update request is rejected.
The client has to query the latest version of preferences, and re-send any changes it wants to make.
The web console will use this to save instance name, description and colour as the first step, and the server will take these from preferences instead of env variables.
Later we can move reloadable configuration or other environment variables into preferences, and build a screen in the web console to manage them.
This PR also contains some refactorings:
Example API calls:
Required by:
https://github.com/questdb/questdb-enterprise/pull/614
questdb/ui#427
TODO:
/settings
endpoint - @glasstigermode
which can have two values:overwrite
andmerge
.overwrite
replaces the config with the one sent,merge
is treated as an incremental update with dedup (new value wins) - @glasstiger