Introducing golem-js 2.0
The new golem-js
beta release has been finalized and released as latest on NPM. In this post, we want to highlight just some of the features and improvements released in this version.
The update of related tooling like the React SDK, our CLI, and create-golem-app will follow shortly after this release.
We also prepared a few additional reference application implementations that will be based on golem-js@2.0
, stay tuned for the next updates.
New features
List and accept invoices for work
In some cases, Requestors could fail to accept the invoices from the Providers - especially if they set their budget too low and request too much work on the network.
While the root cause is being dealt with by the Golem Team, we delivered an interim solution within the SDK.
With the SDK You can now programmatically access the list of invoices from your Yagna node and accept them to process the payment:
import { InvoiceProcessor } from "@golem-sdk/golem-js";
const applyAdditionalFilter = (i: any) => i;
const processor = await InvoiceProcessor.create({
apiKey: "your-app-key-from-Yagna",
});
const invoices = await processor.collectInvoices({
limit: 10,
minAmount: 0.001,
statuses: ["RECEIVED"],
maxAmount: 10,
paymentPlatforms: ["erc20-polygon-glm"],
});
const selectedInvoices = applyAdditionalFilter(invoices);
console.log("Matched invoices", selectedInvoices);
const acceptResults = await processor.acceptManyInvoices({
invoices: selectedInvoices,
});
console.log("Results of accepting invoices", acceptResults);
At the same time, Providers can use this API to browse through the invoices that their node has issued.
Implementing this feature opened the way for a CLI command that helps in browsing and accepting invoices from Providers.
Catch and handle Golem-specific errors
Operating in a decentralized distributed computing space inherently brings a lot of challenges related to error handling for developers.
Widely distributed infrastructure resources with non-deterministic availability characteristics introduce enormous complexity levels that have to be considered.
That's why with this release we introduced a set of custom error types that aim to:
- give you more information about "What?" and "Where?" went wrong
- give you the possibility to handle the error in a more clean manner
And here's one way you can make use of the provided error types:
import { GolemPlatformError, TaskExecutor } from "@golem-sdk/golem-js";
const executor = await TaskExecutor.create({
package: "golem/node:20",
});
try {
console.log("Scheduling work on Golem");
const nodeVersion = await executor.run(async (ctx) => {
// 🆕 🤩 NEW: You can reach provider information (id, name, walletAddress) like that
console.log("Reading node version on provider", ctx.provider.name);
const result = await ctx.run("node -v");
return result.stdout?.toString();
});
console.log("Node version on provider", nodeVersion);
} catch (err) {
// 🆕 🤩 NEW: You can now capture errors from the SDK and handle it as you wish
if (err instanceof GolemPlatformError) {
console.log("It seems that there's an internal error", err);
} else {
console.error("A generic error happened", err);
}
} finally {
// 🧠 REMEMBER: Always do a clean shutdown :)
await executor.shutdown();
}
The possible errors include: GolemError
, GolemUserError
, GolemAbortError
, GolemConfigError
, GolemInternalError
, GolemPlatformError
, GolemTimeoutError
, GolemModuleError
.
Please refer to the API reference to learn more about the semantics of these error types.
Bug-fixes
This release includes numerous bug fixes to improve your experience with Golem Network and the SDK itself. Adopting some of the fixes will require some work at your end, but the changes are worth it.
Deduplication of proposals on the market
That's probably one of the most important changes in this release. Due to the decentralized nature of Golem, each Yagna node can contain multiple offers from the same Provider for the same hardware setup. Previously the SDK attempted to negotiate with the same Provider all the offers that were available from that Provider.
With this release, that's no longer the case. Before the proposal is passed to your proposalFilter
, the SDK de-duplicates the proposals from the same provider and provides you with the most attractive one (lowest in price, with the highest validity time of the proposal).
This way we expect to reduce the overall amount of excessive negotiations on the network and supply the Requestors with the best proposals available.
Market proposal filters
- The market proposal filter function passed to the
TaskExecutor
should not be an asynchronous one. We expect users to provide filter functions that will return boolean values instead of functions with complex async logic to ensure faster processing of the market proposals. - The previous naming scheme and behavior of the proposal filters built into the SDK have been confusing for the users, and we addressed that by rewriting and renaming them.
TaskExecutor
stability
- We fixed the issue that was allowing
TaskExecutor
to continue operation when there was a bug in the user-provided debit note or invoice filters. This was causing activities to be silently terminated due to failing to accept debit notes on time or producing unpaid invoices. After the change, if any of these filters throws an error when called, the wholeTaskExecutor
will be canceled to prevent further malfunctioning operations. - We fixed the issue when the number of retries for a task sent to the
TaskExecutor
was not respected due to timeouts. After the change the task is retried the right amount of times, each time having the configured timeout available to finish - timeouts are applied per attempt to execute the task.
These are just a few of the improvements we provided. Feel free to check the release notes to learn more.
Breaking changes
Unfortunately, making things better sometimes means that we need to break compatibility with the old code. Here's the list of the Backward-Compatibility breaking changes made in this release:
General
- The SDK now requires Yagna at least in version
0.13.2
and will throw an error if you're using a version that's too old. - The SDK won't log anything to the console by default and will use
debug
package to implement logging. - The events accessible via
EventTarget
passed toTaskExecutor
are now going to contain aprovider: ProviderInfo
property that will be enhanced with thewalletAddress
property. OldproviderId
andproviderName
fields are removed from the event.
Task Executor
- The
TaskExecutor
will now be canceled when an issue is raised with processing the payments - including errors in the invoice and debit note filters. - Deprecated
TaskExecutor.isSubprocess
option is now removed. - Deprecated
TaskExecutor.end
is now removed (useTaskExecutor.shutdown
instead) - Deprecated
TaskExecutor.beforeEach
is now removed (useTaskExecutor.onActivityReady
instead) - Deprecated
WorkContext.rejectResult
method is removed.
Market
The following filter re-names were introduced:
ProposalFilters
was renamed toProposalFilterFactory
.acceptAllProposalFilter
was renamed toacceptAll
and no longer async.blackListProposalIdsFilter
was renamed todisallowProvidersById
and no longer async.blackListProposalNamesFilter
was renameddisallowProvidersByName
and no longer async.blackListProposalRegexpFilter
was renamed todisallowProvidersByNameRegex
and no longer async.whiteListProposalIdsFilter
was renamed toallowProvidersById
and no longer async.whiteListProposalNamesFilter
was renamed toallowProvidersByName
and no longer async.whiteListProposalRegexpFilter
was renamed toallowProvidersByNameRegex
and no longer async.
Upgrade instructions
In your existing project run:
npm i --save @golem-sdk/golem-js@^2
After the SDK gets updated, take a look at the list of breaking changes mentioned above and in the release notes. Update your code accordingly, by:
- following the renames of classes and methods
- stop using removed methods and switch to their appointed replacements