Skip to content

Commit be227e3

Browse files
committed
feat: implement Nitro's getExternalMemorySize
1 parent a8b6efc commit be227e3

4 files changed

Lines changed: 137 additions & 0 deletions

File tree

package/cpp/specs/HybridNitroSQLite.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,12 @@ std::shared_ptr<Promise<FileLoadResult>> HybridNitroSQLite::loadFileAsync(const
9191
});
9292
};
9393

94+
size_t HybridNitroSQLite::getExternalMemorySize() noexcept {
95+
// The HybridNitroSQLite instance itself does not own any heavy external
96+
// memory. Database connections are tracked globally in `dbMap`.
97+
// We still report the size of this instance so the JS GC can take it
98+
// into account when estimating native memory pressure.
99+
return sizeof(*this);
100+
}
101+
94102
} // namespace margelo::nitro::rnnitrosqlite

package/cpp/specs/HybridNitroSQLite.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ class HybridNitroSQLite : public HybridNitroSQLiteSpec {
4040

4141
FileLoadResult loadFile(const std::string& dbName, const std::string& location) override;
4242
std::shared_ptr<Promise<FileLoadResult>> loadFileAsync(const std::string& dbName, const std::string& location) override;
43+
44+
/**
45+
* Approximate the native memory used by this Hybrid Object.
46+
*
47+
* This object itself does not own any large external resources – database
48+
* connections are handled by the shared `dbMap` in `operations.cpp`.
49+
* We still report the size of this C++ instance so the JS GC can account
50+
* for the object when it is retained from JS.
51+
*/
52+
size_t getExternalMemorySize() noexcept override;
4353
};
4454

4555
inline std::string HybridNitroSQLite::docPath = "";

package/cpp/specs/HybridNitroSQLiteQueryResult.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,98 @@
88

99
namespace margelo::nitro::rnnitrosqlite {
1010

11+
namespace {
12+
13+
/**
14+
* Compute the approximate external memory size of a single SQLiteValue.
15+
*
16+
* We only need to account for heap allocations that can actually put
17+
* pressure on the JS GC – in practice, that means:
18+
* - `std::string` contents,
19+
* - `ArrayBuffer` instances (object + underlying bytes).
20+
*
21+
* Other variants (`NullType`, `bool`, `double`) are small and live inline.
22+
*/
23+
size_t getValueExternalMemorySize(const SQLiteValue& value) {
24+
if (std::holds_alternative<std::string>(value)) {
25+
const auto& stringValue = std::get<std::string>(value);
26+
return stringValue.capacity();
27+
}
28+
29+
if (std::holds_alternative<std::shared_ptr<ArrayBuffer>>(value)) {
30+
const auto& buffer = std::get<std::shared_ptr<ArrayBuffer>>(value);
31+
if (!buffer) {
32+
return 0;
33+
}
34+
35+
const auto bufferSize = buffer->size();
36+
return sizeof(*buffer.get()) + bufferSize;
37+
}
38+
39+
return 0;
40+
}
41+
42+
/**
43+
* Compute the approximate external memory size of a single result row.
44+
* This includes:
45+
* - Column name string capacities,
46+
* - Heap usage for the actual SQLiteValue contents.
47+
*/
48+
size_t getRowExternalMemorySize(const SQLiteQueryResultRow& row) {
49+
size_t size = 0;
50+
51+
for (const auto& entry : row) {
52+
const auto& columnName = entry.first;
53+
const auto& value = entry.second;
54+
55+
size += columnName.capacity();
56+
size += getValueExternalMemorySize(value);
57+
}
58+
59+
return size;
60+
}
61+
62+
/**
63+
* Compute the approximate external memory size of the full result set.
64+
* We add:
65+
* - The vector's backing storage,
66+
* - All rows (column names + values).
67+
*/
68+
size_t getResultsExternalMemorySize(const SQLiteQueryResults& results) {
69+
size_t size = 0;
70+
71+
const auto resultCapacity = results.capacity();
72+
size += resultCapacity * sizeof(SQLiteQueryResultRow);
73+
74+
for (const auto& row : results) {
75+
size += getRowExternalMemorySize(row);
76+
}
77+
78+
return size;
79+
}
80+
81+
/**
82+
* Compute the approximate external memory size of the table metadata.
83+
* We include:
84+
* - Column name string capacities (map keys),
85+
* - Metadata contents, especially the `name` string on each metadata entry.
86+
*/
87+
size_t getMetadataExternalMemorySize(const SQLiteQueryTableMetadata& metadata) {
88+
size_t size = 0;
89+
90+
for (const auto& entry : metadata) {
91+
const auto& columnName = entry.first;
92+
const auto& columnMeta = entry.second;
93+
94+
size += columnName.capacity();
95+
size += columnMeta.name.capacity();
96+
}
97+
98+
return size;
99+
}
100+
101+
} // namespace
102+
11103
std::optional<double> HybridNitroSQLiteQueryResult::getInsertId() {
12104
return _insertId;
13105
}
@@ -24,4 +116,16 @@ std::optional<SQLiteQueryTableMetadata> HybridNitroSQLiteQueryResult::getMetadat
24116
return _metadata;
25117
}
26118

119+
size_t HybridNitroSQLiteQueryResult::getExternalMemorySize() noexcept {
120+
size_t size = sizeof(*this);
121+
122+
size += getResultsExternalMemorySize(_results);
123+
124+
if (_metadata) {
125+
size += getMetadataExternalMemorySize(*_metadata);
126+
}
127+
128+
return size;
129+
}
130+
27131
} // namespace margelo::nitro::rnnitrosqlite

package/cpp/specs/HybridNitroSQLiteQueryResult.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ class HybridNitroSQLiteQueryResult : public HybridNitroSQLiteQueryResultSpec {
2727
double getRowsAffected() override;
2828
SQLiteQueryResults getResults() override;
2929
std::optional<SQLiteQueryTableMetadata> getMetadata() override;
30+
31+
/**
32+
* Approximate the native memory used by this query result.
33+
*
34+
* We account for:
35+
* - The size of this C++ object (`sizeof(*this)`),
36+
* - All rows and columns (including column name strings),
37+
* - String values stored in the result set,
38+
* - ArrayBuffers used for BLOB columns (object overhead + raw byte size),
39+
* - Column metadata strings.
40+
*
41+
* This is a best-effort estimate and intentionally focuses on external
42+
* heap allocations that can put pressure on the JS GC.
43+
*/
44+
size_t getExternalMemorySize() noexcept override;
3045
};
3146

3247
} // namespace margelo::nitro::rnnitrosqlite

0 commit comments

Comments
 (0)