Skip to content
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

Add TairHash basic support #5189

Open
wants to merge 1 commit into
base: 2022
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/models/key-models/abstractkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class KeyModel : public ValueEditor::Model {

virtual void loadRows(QVariant rowStart, unsigned long count,
LoadRowsCallback callback) override {
if (m_rowsLoadCmd.mid(1, 4).toLower() == "scan") {
if (m_rowsLoadCmd.right(4).toLower() == "scan") {
QList<QByteArray> cmdParts = {m_rowsLoadCmd, m_keyFullPath,
QString::number(m_scanCursor).toLatin1(),
"COUNT", QString::number(count).toLatin1()};
Expand Down
4 changes: 4 additions & 0 deletions src/app/models/key-models/keyfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "sortedsetkey.h"
#include "stream.h"
#include "stringkey.h"
#include "tairhashkey.h"

KeyFactory::KeyFactory() {}

Expand Down Expand Up @@ -158,6 +159,9 @@ QSharedPointer<ValueEditor::Model> KeyFactory::createModel(
} else if (type == "stream") {
return QSharedPointer<ValueEditor::Model>(
new StreamKeyModel(connection, keyFullPath, dbIndex, ttl));
} else if (type == "tairhash-") {
return QSharedPointer<ValueEditor::Model>(
new TairHashKeyModel(connection, keyFullPath, dbIndex, ttl));
}

return QSharedPointer<ValueEditor::Model>();
Expand Down
189 changes: 189 additions & 0 deletions src/app/models/key-models/tairhashkey.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#include "tairhashkey.h"
#include <qredisclient/connection.h>
#include <QObject>

TairHashKeyModel::TairHashKeyModel(QSharedPointer<RedisClient::Connection> connection,
QByteArray fullPath, int dbIndex, long long ttl)
: KeyModel(connection, fullPath, dbIndex, ttl, "EXHLEN", "EXHSCAN") {}

QString TairHashKeyModel::type() { return "tairhash"; }

QStringList TairHashKeyModel::getColumnNames() {
return QStringList() << "rowNumber"
<< "key"
<< "value"
<< "metadata";
}

QHash<int, QByteArray> TairHashKeyModel::getRoles() {
QHash<int, QByteArray> roles;
roles[Roles::RowNumber] = "rowNumber";
roles[Roles::Key] = "key";
roles[Roles::Value] = "value";
roles[Roles::TTL] = "ttl";
roles[Roles::Version] = "version";
return roles;
}

QVariant TairHashKeyModel::getData(int rowIndex, int dataRole) {
if (!isRowLoaded(rowIndex)) return QVariant();

auto row = m_rowsCache[rowIndex];

if (dataRole == Roles::Key)
return row.first;
else if (dataRole == Roles::Value)
return row.second.value;
else if (dataRole == Roles::TTL)
return row.second.ttl;
else if (dataRole == Roles::Version)
return row.second.version;
else if (dataRole == Roles::RowNumber)
return rowIndex;

return QVariant();
}

void TairHashKeyModel::updateRow(int rowIndex, const QVariantMap &row, Callback c) {
if (!isRowLoaded(rowIndex) || !isRowValid(row)) {
c(QCoreApplication::translate("RESP", "Invalid row"));
return;
}

auto cachedRow = m_rowsCache[rowIndex];

bool keyChanged = cachedRow.first != row["key"].toByteArray();
bool valueChanged = cachedRow.second.value != row["value"].toByteArray();

TairHashValue newValue;

if (valueChanged) {
newValue = {row["value"].toByteArray(), cachedRow.second.ttl,
cachedRow.second.version};
} else {
newValue = cachedRow.second;
}

QPair<QByteArray, TairHashValue> newRow(
(keyChanged) ? row["key"].toByteArray() : cachedRow.first, newValue
);

auto afterValueUpdate = [this, c, rowIndex, newRow](const QString &err) {
if (err.isEmpty()) {
m_rowsCache.replace(rowIndex, newRow);
loadMetadata(newRow.first, rowIndex);
}

return c(err);
};

if (keyChanged) {
deleteHashRow(cachedRow.first,
[this, c, newRow, afterValueUpdate](const QString &err) {
if (err.size() > 0) return c(err);

setHashRow(newRow.first, newRow.second.value, afterValueUpdate);
});
} else {
setHashRow(newRow.first, newRow.second.value, afterValueUpdate);
}
}

void TairHashKeyModel::addRow(const QVariantMap &row, Callback c) {
if (!isRowValid(row)) {
c(QCoreApplication::translate("RESP", "Invalid row"));
return;
}

setHashRow(
row["key"].toByteArray(), row["value"].toByteArray(),
[this, c](const QString &err) {
if (err.isEmpty()) m_rowCount++;
return c(err);
},
false);
}

void TairHashKeyModel::removeRow(int i, Callback c) {
if (!isRowLoaded(i)) return;

auto row = m_rowsCache[i];

deleteHashRow(row.first, [this, i, c](const QString &err) {
if (err.isEmpty()) {
m_rowCount--;
m_rowsCache.removeAt(i);
setRemovedIfEmpty();
}

return c(err);
});
}

void TairHashKeyModel::setHashRow(const QByteArray &hashKey,
const QByteArray &hashValue, Callback c,
bool updateIfNotExist) {
QList<QByteArray> rawCmd{(updateIfNotExist) ? "EXHSET" : "EXHSETNX",
m_keyFullPath, hashKey, hashValue};

executeCmd(rawCmd, c,
[updateIfNotExist](RedisClient::Response r, Callback c) {
if (updateIfNotExist == false && r.value().toInt() == 0) {
return c(QCoreApplication::translate(
"RESP", "Value with the same key already exists"));
} else {
return c(QString());
}
});
}

void TairHashKeyModel::deleteHashRow(const QByteArray &hashKey, Callback c) {
executeCmd({"EXHDEL", m_keyFullPath, hashKey}, c);
}

void TairHashKeyModel::loadMetadata(const QByteArray &hashKey, int rowIndex) {
executeCmd({"EXHVER", m_keyFullPath, hashKey}, Callback{},
[rowIndex, this](RedisClient::Response r, Callback) {
auto row = m_rowsCache.getRow(rowIndex);
row.second.version = r.value().toLongLong();
m_rowsCache.replace(rowIndex, row);
});

executeCmd({"EXHTTL", m_keyFullPath, hashKey}, Callback{},
[rowIndex, this](RedisClient::Response r, Callback) {
auto row = m_rowsCache.getRow(rowIndex);
row.second.ttl = r.value().toLongLong();
m_rowsCache.replace(rowIndex, row);
});
}

int TairHashKeyModel::addLoadedRowsToCache(const QVariantList &rows,
QVariant rowStartId) {
QList<QPair<QByteArray, TairHashValue>> result;

for (QVariantList::const_iterator item = rows.begin(); item != rows.end();
++item) {
QPair<QByteArray, TairHashValue> value;
QByteArray hashKey = item->toByteArray();
value.first = hashKey;
++item;

int currentIndex = result.size();

if (item == rows.end()) {
emit m_notifier->error(QCoreApplication::translate(
"RESP", "Data was loaded from server partially."));
return 0;
}

value.second = TairHashValue{item->toByteArray(), -1, -1};
result.push_back(value);

loadMetadata(value.first, currentIndex);
}

auto rowStart = rowStartId.toLongLong();
m_rowsCache.addLoadedRange({rowStart, rowStart + result.size() - 1}, result);

return result.size();
}
36 changes: 36 additions & 0 deletions src/app/models/key-models/tairhashkey.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once
#include "abstractkey.h"

struct TairHashValue {
QByteArray value;
qlonglong ttl;
qlonglong version;
};

class TairHashKeyModel : public KeyModel<QPair<QByteArray, TairHashValue>> {
public:
TairHashKeyModel(QSharedPointer<RedisClient::Connection> connection,
QByteArray fullPath, int dbIndex, long long ttl);

QString type() override;
QStringList getColumnNames() override;
QHash<int, QByteArray> getRoles() override;

QVariant getData(int rowIndex, int dataRole) override;
void addRow(const QVariantMap &, Callback) override;
virtual void updateRow(int rowIndex, const QVariantMap &, Callback) override;
void removeRow(int, Callback) override;

protected:
int addLoadedRowsToCache(const QVariantList &list,
QVariant rowStart) override;

private:
enum Roles { RowNumber = Qt::UserRole + 1, Key, Value, TTL, Version };

void setHashRow(const QByteArray &hashKey, const QByteArray &hashValue,
Callback c, bool updateIfNotExist = true);
void deleteHashRow(const QByteArray &hashKey, Callback c);

void loadMetadata(const QByteArray& hashKey, int row);
};
38 changes: 35 additions & 3 deletions src/qml/value-editor/ValueTable.qml
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,9 @@ Item {
property int totalPages: keyTab.keyModel ? Math.ceil(keyTab.keyModel.totalRowCount / maxItemsOnPage) : 0
property bool forceLoading: false
property int firstColumnWidth: 75
property int valueColumnWidth: keyTab.keyModel && keyTab.keyModel.columnNames.length == 2? root.width - 200 - table.firstColumnWidth - table.columnSpacing
: (root.width - 200 - table.firstColumnWidth - table.columnSpacing) / 2
property int valueColumnWidth: keyTab.keyModel && table.columns > 2 ? (root.width - 200 - table.firstColumnWidth - table.columnSpacing) / (table.columns - 1)
: root.width - 200 - table.firstColumnWidth - table.columnSpacing

property var valueColumnWidthOverrides: QtObject {}

Keys.onUpPressed: {
Expand Down Expand Up @@ -239,7 +240,7 @@ Item {
if (keyTab.keyModel.columnNames.length == 2) {
table.valueColumnWidthOverrides[index] = width;
} else {
for (var i=1; i < 3; i++)
for (var i=1; i < keyTab.keyModel.columnNames.length; i++)
{
if (i === index) {
table.valueColumnWidthOverrides[i] = width;
Expand Down Expand Up @@ -333,6 +334,37 @@ Item {
}
}
}

DelegateChoice {
column: 3

ValueTableCell {
objectName: "rdm_value_table_cell_col4"
implicitWidth: table.valueColumnWidth
implicitHeight: 30

selected: table.currentRow === row
onClicked: {
table.currentRow = row
table.forceActiveFocus()
}

textDelegate: Component {
TextEdit {
wrapMode: Text.WrapAnywhere
color: root.selected ? sysPalette.highlightedText : sysPalette.text
readOnly: true
selectByMouse: true
text: {
if (display === "" || !isMultiRow) {
return ""
}
return "TTL:" + model.ttl + "\nVersion:" + model.version
}
}
}
}
}
}

OkDialogOverlay {
Expand Down
20 changes: 13 additions & 7 deletions src/qml/value-editor/ValueTableCell.qml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import QtQuick.Controls 2.0
Item {
id: root

property alias text: textItem.text
property string text
property alias textDelegate: textItem.sourceComponent
property alias color: background.color
property bool selected: false

Expand All @@ -18,14 +19,19 @@ Item {
color: root.selected ? sysPalette.highlight : sysPalette.base
clip: true

TextInput {
Loader {
id: textItem
anchors.centerIn: parent
wrapMode: Text.WrapAnywhere
color: root.selected ? sysPalette.highlightedText : sysPalette.text
readOnly: true
selectByMouse: true
autoScroll: false
sourceComponent: Component {
TextInput {
wrapMode: Text.WrapAnywhere
color: root.selected ? sysPalette.highlightedText : sysPalette.text
readOnly: true
selectByMouse: true
text: root.text
autoScroll: false
}
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/qml/value-editor/editors/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function getEditorByTypeString(keyType) {
return "./editors/SingleItemEditor.qml"
} else if (keyType === "zset") {
return "./editors/SortedSetItemEditor.qml"
} else if (keyType === "hash") {
} else if (keyType === "hash" || keyType === "tairhash") {
return "./editors/HashItemEditor.qml"
} else if (keyType === "stream") {
return "./editors/StreamItemEditor.qml"
Expand Down