Coder Social home page Coder Social logo

golemfactory / golem-js Goto Github PK

View Code? Open in Web Editor NEW
29.0 16.0 16.0 46.09 MB

TypeScript + NodeJS API for Golem

Home Page: https://docs.golem.network/docs/creators/javascript

License: GNU Lesser General Public License v3.0

TypeScript 98.37% JavaScript 0.98% Shell 0.07% Dockerfile 0.50% Python 0.08%
golem typescript nodejs distributed-computing sdk

golem-js's Introduction

Golem JavaScript API

GitHub npm node-current npm type definitions GitHub Workflow Status GitHub issues Discord

Table of contents

What's Golem and golem-js?

The Golem Network fosters a global group of creators building ambitious software solutions that will shape the technological landscape of future generations by accessing computing resources across the platform. Golem Network is an accessible, reliable, open access and censorship-resistant protocol, democratizing access to digital resources and connecting users through a flexible, open-source platform.

golem-js is the JavaScript API that allows developers to connect to their Golem nodes and manage their distributed, computational loads through Golem Network.

System requirements

To use golem-js, it is necessary to have yagna installed, with a recommended minimum version of v0.14.0. Yagna is a service that communicates and performs operations on the Golem Network, upon your requests via the SDK. You can follow these instructions to set it up.

Simplified installation steps

In order to get started and on Golem Network and obtain test GLM tokens (tGLM) that will allow you to build on the test network, follow these steps:

Join the network as a requestor and obtain test tokens

# Join the network as a requestor
curl -sSf https://join.golem.network/as-requestor | bash -

# Start the golem node on your machine,
# you can use `daemonize` to run this in background
yagna service run

# IN SEPARATE TERMINAL (if not daemonized)
# Initialize your requestor
yagna payment init --sender --network holesky

# Request funds on the test network
yagna payment fund --network holesky

# Check the status of the funds
yagna payment status --network holesky

Obtain your app-key to use with SDK

If you don't have any app-keys available from yagna app-key list, go ahead and create one with the command below. You will need this key in order to communicate with yagna from your application via golem-js.You can set it as YAGNA_APPKEY environment variable.

yagna app-key create my-golem-app

Installation

@golem-sdk/golem-js is available as a NPM package.

You can install it through npm:

npm install @golem-sdk/golem-js

or by yarn:

yarn add @golem-sdk/golem-js

Building

To build a library available to the NodeJS environment:

npm run build
# or
yarn build

This will generate production code in the dist/ directory ready to be used in your nodejs or browser applications.

Usage

Hello World example

import { TaskExecutor } from "@golem-sdk/golem-js";

(async function main() {
  const executor = await TaskExecutor.create("golem/alpine:latest");
  try {
    await executor.run(async (ctx) => console.log((await ctx.run("echo 'Hello World'")).stdout));
  } catch (error) {
    console.error("Computation failed:", error);
  } finally {
    await executor.shutdown();
  }
})();

More examples

The examples directory in the repository contains various usage patterns for the SDK. You can browse through them and learn about the recommended practices. All examples are automatically tested during our release process.

In case you find an issue with the examples, feel free to submit an issue report to the repository.

You can find even more examples and tutorials in the JavaScript API section of the Golem Network Docs.

Supported environments

The SDK is designed to work with LTS versions of Node (starting from 18) and with browsers.

Golem Network Market Basics

The Golem Network provides an open marketplace where anyone can join as a Provider and supply the network with their computing power. In return for their service, they are billing Requestors (users of this SDK) according to the pricing that they define.

As a Requestor, you might want to:

  • control the limit price so that you're not going to over-spend your funds
  • control the interactions with the providers if you have a list of the ones which you like or the ones which you would like to avoid

To make this easy, we provided you with a set of predefined market proposal filters, which you can combine to implement your own market strategy (described below).

Mid-agreement payments to the Providers for used resources

When you obtain resources from the Provider and start using them, the billing cycle will start immediately. Since reliable service and payments are important for all actors in the Golem Network, the SDK makes use of the mid-agreement payments model and implements best practices for the market, which include:

  • responding and accepting debit notes for activities that last longer than 30 minutes
  • issuing mid-agreement payments (pay-as-you-go)

By default, the SDK will:

  • accept debit notes sent by the Providers within two minutes of receipt (so that the Provider knows that we're alive, and it will continue serving the resources)
  • issue a mid-agreement payment every 12 hours (so that the provider will be paid on a regular interval for serving the resources for more than 10 hours)

You can learn more about the mid-agreement and other payment models from the official docs.

These values are defaults and can be influenced by the following settings:

  • DemandOptions.expirationSec
  • DemandOptions.debitNotesAcceptanceTimeoutSec
  • DemandOptions.midAgreementPaymentTimeoutSec

If you're using TaskExecutor to run tasks on Golem, you can pass them as part of the configuration object accepted by TaskExecutor.create. Consult JS API reference for details.

Limit price limits to filter out offers that are too expensive

import { TaskExecutor, ProposalFilterFactory } from "@golem-sdk/golem-js";

const executor = await TaskExecutor.create({
  // What do you want to run
  package: "golem/alpine:3.18.2",

  // How much you wish to spend
  budget: 0.5,
  proposalFilter: ProposalFilterFactory.limitPriceFilter({
    start: 1,
    cpuPerSec: 1 / 3600,
    envPerSec: 1 / 3600,
  }),

  // Where you want to spend
  payment: {
    network: "polygon",
  },
});

To learn more about other filters, please check the API reference of the market/strategy module

Work with reliable providers

The getHealthyProvidersWhiteList helper will provide you with a list of Provider ID's that were checked with basic health-checks. Using this whitelist will increase the chance of working with a reliable provider. Please note, that you can also build up your own list of favourite providers and use it in a similar fashion.

import { MarketHelpers, ProposalFilterFactory, TaskExecutor } from "@golem-sdk/golem-js";

// Collect the whitelist
const verifiedProviders = await MarketHelpers.getHealthyProvidersWhiteList();

// Prepare the whitelist filter
const whiteList = ProposalFilterFactory.allowProvidersById(verifiedProviders);

// Prepare the price filter
const acceptablePrice = ProposalFilterFactory.limitPriceFilter({
  start: 1,
  cpuPerSec: 1 / 3600,
  envPerSec: 1 / 3600,
});

const executor = await TaskExecutor.create({
  // What do you want to run
  package: "golem/alpine:3.18.2",

  // How much you wish to spend
  budget: 0.5,
  proposalFilter: (proposal) => acceptablePrice(proposal) && whiteList(proposal),

  // Where you want to spend
  payment: {
    network: "polygon",
  },
});

Debugging

The SDK uses the debug package to provide debug logs. To enable them, set the DEBUG environment variable to golem-js:* or golem-js:market:* to see all logs or only the market-related ones, respectively. For more information, please refer to the debug package documentation.

Testing

Read the dedicated testing documentation to learn more about how to run tests of the SDK.

Contributing

It is recommended to run unit tests and static code analysis before committing changes.

yarn lint
# and
yarn format

See also

golem-js's People

Contributors

azawlocki avatar braunmann avatar cryptobench avatar dependabot[bot] avatar etam avatar figurestudios avatar filipgolem avatar grisha87 avatar jalas167 avatar kamirr avatar kmazurek avatar mateuszsrebrny avatar mdtanrikulu avatar mfranciszkiewicz avatar mgordel avatar pgrzy-golem avatar pociej avatar prekucki avatar sewerynkras avatar shadeofblue avatar stranger80 avatar vandavv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

golem-js's Issues

No graceful shutdown after a TimeoutError

When [yajsapi] error: fail= TimeoutError: task timeout exceeded. timeout=900000 happens, yajsapi displays errors like:

  • HTTP 500 (from getExecBatchResults)
  • Error [ERR_STREAM_DESTROYED]: Cannot call write after a stream was destroyed (gftp is closed too early)
  • [yajsapi] error: Failed to destroy activity: ...

yajsapi catching up with yapapi

Implementations


  • ExecutorEvents
  • SmartQueue + Consumer + AsyncLock
  • asyncWrapper



Updates


  • WorkContext
  • Activity
  • Blender example
  • Project Structure changes <Callable (util), Task, Task Status, EventLoop(util)>



"Double payment" strikes back

In testing session we catched Double payment again but in different form this time, both payments had value.

2020-11-19 13:08:31 [yajsapi] info: Accepted payment: 0.03736446749000000 for invoice e480fb53-7e36-455
2020-11-19 13:08:31 [yajsapi] info: Total cost: 0.09931349073911111
2020-11-19 13:08:31 [yajsapi] info: Accepted payment: 0.03508967915111111 for invoice 9f473075-3990-4b0
2020-11-19 13:08:31 [yajsapi] info: Total cost: 0.10234954429411111
2020-11-19 13:08:31 [yajsapi] info: Accepted payment: 0.003036053555000000 for invoice 14015461-32f3-429

Payment phase lasted too long

Payment phase lasted for 66s which is twice as much as market negotiations and activity together 34.1s

$ YAGNA_APPKEY=$(yagna app-key list --json | jq -r .values[0][1]) yarn run ts:blender
yarn run v1.22.10
$ ts-node-script ./blender/blender.ts
Using subnet: community.3
[...]
result= output_0.png
2020-11-19 13:00:14 [yajsapi] info: Computation finished in 34.1s
2020-11-19 13:00:14 [yajsapi] info: Negotiated 2 agreements with 2 providers
2020-11-19 13:00:14 [yajsapi] info: Provider 'friendly-error' computed 4 tasks
2020-11-19 13:00:14 [yajsapi] info: Provider 'univac.3' computed 2 tasks
2020-11-19 13:00:14 [yajsapi] info: Provider 'manchester.3' did not compute any tasks
2020-11-19 13:00:14 [yajsapi] info: Accepted payment: 0.06630091567800001 for invoice fa479baf-99c3-42e
2020-11-19 13:00:14 [yajsapi] info: Total cost: 0.11400723553883334
2020-11-19 13:00:14 [yajsapi] info: Accepted payment: 0.04770631986083333 for invoice ee3d7025-17cf-4f7
2020-11-19 13:00:16 [yajsapi] info: Received proposals from 6 providers so far
2020-11-19 13:00:20 [yajsapi] info: Received proposals from 6 providers so far
2020-11-19 13:00:22 [yajsapi] info: Received proposals from 6 providers so far
2020-11-19 13:00:26 [yajsapi] info: Received proposals from 7 providers so far
  Done in 110.11s.

add a @deprectated decorator

as part of our backwards compatibility flow, we should include a deprecation notice for any classes, methods and fuctions that we want to remove in a future version of our API

to that end, we'd like to have a @deprecated decorator which emits a WARNING log each time such deprectated component is called/instantiated/etc

long tasks frequently fail on providers

initial research: may be caused by not repeating the getExecBatchResults call after the timeout happens.

fixed by:
#90

example (video conversion):

  async function* worker(ctx: WorkContext, tasks) {
    let sentFile = "/golem/resource/input.file";

    let sendPath = path.join(__dirname, "./input.mp4");

    //ctx.log(`Sending file: ${sendPath}`);

    ctx.send_file(sendPath,sentFile);

    //ctx.log(`Sent: ${sendPath}`);

    for await (let task of tasks) {
      let outputNum: any = task.data();
      let presets = ["Fast 480p30", "VP9 MKV 1080p30"];
      let preset = "Fast 480p30";
      let outputFile = `output_${outputNum}.mkv`;
      let downloadLog = path.join(__dirname, `log_${outputNum}.txt`);
      let downloadFile2 = path.join(__dirname, `output_${outputNum}.txt`);

      //ctx.log(`output num: ${outputNum}`);
      //ctx.log(`downloading to ${downloadLog}`);

      let commands = [
        "-c",
        //`exec >/golem/output/output.txt 2>&1;
        `cd /golem/output/;
        echo $PWD > log.txt;
        echo preset:${preset} >> log.txt;
        echo output_file:${outputFile} >> log.txt;
        ls -lah ${sentFile} >> log.txt;
        which HandBrakeCLI >> log.txt;
        HandBrakeCLI -i ${sentFile} -o ${outputFile} --preset '${preset}' >> log.txt 2>&1;
        ls -lah >> log.txt;`
      ]

      //ctx.log("Running commands:");
      //ctx.log(commands);

      ctx.run("/bin/sh", commands);
      ctx.log("commands done");

      ctx.log("downloading");
      ctx.download_file("/golem/output/log.txt", downloadLog);
      ctx.download_file(`/golem/output/${outputFile}`,path.join(__dirname, outputFile));
      ctx.log("downloading done");

      yield ctx.commit();
      // TODO: Check
      // job results are valid // and reject by:
      // task.reject_task(msg = 'invalid file')
      task.accept_task(outputFile);
    }

    ctx.log("no more frames to render");
    return;
  }

(win-js) additional ctrl c was needed when stopping computations

yagna_rCURRENT.log
yajsapi.log

@ederenn - Win 10+ js

Terminate batch job (Y/N)? 2021-02-11 12:37:15 [yajsapi] error: Computation interrupted by the user.
2021-02-11 12:37:15 [yajsapi] info: Computation finished in 353.3s
2021-02-11 12:37:15 [yajsapi] info: Negotiated 0 agreements with 0 providers
2021-02-11 12:37:15 [yajsapi] info: Provider 'tytus ⤜(ʘ_ʘ)⤏ romek ᕦ  !  ᕥ i atomek' did not compute any tasks
2021-02-11 12:37:15 [yajsapi] info: Provider 'tytus ⤜(ʘ_ʘ)⤏ romek ᕦ  !  ᕥ i atomek' did not compute any tasks
2021-02-11 12:37:15 [yajsapi] info: Provider 'tytus ⤜(ʘ_ʘ)⤏ romek ᕦ  !  ᕥ i atomek' did not compute any tasks
2021-02-11 12:37:15 [yajsapi] info: Provider 'tytus ⤜(ʘ_ʘ)⤏ romek ᕦ  !  ᕥ i atomek' did not compute any tasks
2021-02-11 12:37:27 [yajsapi] debug: GFTP server closed, code=null
2021-02-11 12:37:30 [yajsapi] debug: waitForApproval(431d719045b1b74fb7d919e52220bb0a7f6ebca39c1ee52bbcc08694f4a1581f) raised ApiException Error: Request failed with status code 408

^C
C:\Users\ederenn\Projects\yajsapi\examples>

createAllocation error is not clearly explained

Using subnet: devnet-alpha.4, network: rinkeby, driver: zksync
[yajsapi] debug: Using image repository: _girepo._tcp.dev.golem.network -> http://yacn.dev.golem.network:8000.
[yajsapi] debug: Creating allocation using payment platform zksync-rinkeby-tglm
[yajsapi] error: Request failed with status code 400
[yajsapi] error: Executor - Error: Request failed with status code 400

Error: Error: Request failed with status code 400
    at _AllocationTask.ready (yajsapi/rest/payment.ts:175:13)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

✨  Done in 3.73s.

Fix Winston File transport bug

(node:18568) UnhandledPromiseRejectionWarning: Error: write after end
    at writeAfterEnd (/Users/mdt/projects/Golem/yajsapi/node_modules/readable-stream/lib/_stream_writable.js:261:12)
    at PassThrough.Writable.write (/Users/mdt/projects/Golem/yajsapi/node_modules/readable-stream/lib/_stream_writable.js:305:21)
    at File.log (/Users/mdt/projects/Golem/yajsapi/node_modules/winston/lib/winston/transports/file.js:185:34)
    at File._write (/Users/mdt/projects/Golem/yajsapi/node_modules/winston-transport/index.js:82:19)
    at doWrite (/Users/mdt/projects/Golem/yajsapi/node_modules/winston-transport/node_modules/readable-stream/lib/_stream_writable.js:428:64)
    at writeOrBuffer (/Users/mdt/projects/Golem/yajsapi/node_modules/winston-transport/node_modules/readable-stream/lib/_stream_writable.js:417:5)
    at File.Writable.write (/Users/mdt/projects/Golem/yajsapi/node_modules/winston-transport/node_modules/readable-stream/lib/_stream_writable.js:334:11)
    at DerivedLogger.ondata (/Users/mdt/projects/Golem/yajsapi/node_modules/readable-stream/lib/_stream_readable.js:681:20)
    at DerivedLogger.emit (events.js:327:22)
    at addChunk (/Users/mdt/projects/Golem/yajsapi/node_modules/readable-stream/lib/_stream_readable.js:298:12)
    at readableAddChunk (/Users/mdt/projects/Golem/yajsapi/node_modules/readable-stream/lib/_stream_readable.js:280:11)
    at DerivedLogger.Readable.push (/Users/mdt/projects/Golem/yajsapi/node_modules/readable-stream/lib/_stream_readable.js:241:10)
    at DerivedLogger.Transform.push (/Users/mdt/projects/Golem/yajsapi/node_modules/readable-stream/lib/_stream_transform.js:139:32)
    at DerivedLogger._transform (/Users/mdt/projects/Golem/yajsapi/node_modules/winston/lib/winston/logger.js:305:12)
    at DerivedLogger.Transform._read (/Users/mdt/projects/Golem/yajsapi/node_modules/readable-stream/lib/_stream_transform.js:177:10)
    at DerivedLogger.Transform._write (/Users/mdt/projects/Golem/yajsapi/node_modules/readable-stream/lib/_stream_transform.js:164:83)
(node:18568) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:18568) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

provider_tasks and provider_cost are indexed by provider names, which are not unique

    # Maps a provider name to the list of task ids computed by the provider
    provider_tasks: Dict[str, List[str]]

    # Map a provider name to the sum of amounts in this provider's invoices
    provider_cost: Dict[str, float]

It should map Node_ID to List[str] and Node_ID to float.

also, this code has to map unique Node_IDs to table rows:

    const results = [...this.confirmed_agreements].map(
      (agr_id) => {
        const name = this.agreement_provider_name[agr_id];
        const tasks = this.provider_tasks[name];
        const cost = this.provider_cost[name] || "0 (no invoices?)";
        return {
          'Provider Name': name,
          'Tasks Computed': tasks ? tasks.length : 0,
          'Cost': cost,
        };
      }
    );
    console.table(results);

Pass `--subnet-tag` as argument like yapapi

For testing sessions it is nice to have the option to pass the subnet flag as an argument.

I propose the same name or the argument as yapapi is using:

 --subnet-tag devnet-alpha.2

decrease verbosity of logs

generally, most of the debug logs should only be presented when the example is run with --debug flag
please compare to yapapi's verbosity

Failed to create activity 408 after computation finished

Phillip - OSX catalina, yajsapi latest version - 408 after computation is finished.
GJ - had this too on mac-js

    2021-02-11 12:20:40 [yajsapi] info: Computation finished in 402.1s
    2021-02-11 12:20:40 [yajsapi] info: Negotiated 2 agreements with 2 providers
    2021-02-11 12:20:40 [yajsapi] info: Provider 'tytus ⤜(ʘ_ʘ)⤏ romek ᕦ  !  ᕥ i atomek' computed 6 tasks
    2021-02-11 12:20:40 [yajsapi] info: Provider 'ziomek' did not compute any tasks
    2021-02-11 12:20:40 [yajsapi] info: Provider 'ziomek' did not compute any tasks
    2021-02-11 12:20:40 [yajsapi] info: Provider 'glen.4' did not compute any tasks
    2021-02-11 12:20:40 [yajsapi] info: Provider 'tytus ⤜(ʘ_ʘ)⤏ romek ᕦ  !  ᕥ i atomek' did not compute any tasks
    2021-02-11 12:20:40 [yajsapi] info: Provider 'tytus ⤜(ʘ_ʘ)⤏ romek ᕦ  !  ᕥ i atomek' did not compute any tasks
    2021-02-11 12:20:40 [yajsapi] info: Provider 'glen.4' did not compute any tasks
    2021-02-11 12:20:42 [yajsapi] info: Accepted payment: 0.002062975904072222 for invoice 8545e468-7fe7-4d9
    2021-02-11 12:20:47 [yajsapi] error: Failed to create activity for agreement 1b7cb6cb26391c9f7e4f964df0fe9d7557c2e2839393ee56aae668ba680fbba7: Error: Request failed with status code 408
    2021-02-11 12:20:47 [yajsapi] error: Worker finished with error: Error: Request failed with status code 408
    2021-02-11 12:21:01 [yajsapi] info: Finished waiting for payments. Summary:
    ┌─────────┬──────────────┬──────────────────────────────────────────┬────────────────┬──────────────────────┐
    │ (index) │  Agreement   │              Provider Name               │ Tasks Computed │         Cost         │
    ├─────────┼──────────────┼──────────────────────────────────────────┼────────────────┼──────────────────────┤
    │    0    │ 'd6270cbcdf' │ 'tytus ⤜(ʘ_ʘ)⤏ romek ᕦ  !  ᕥ i atomek' │       6        │ 0.002062975904072222 │
    │    1    │ '1b7cb6cb26' │                 'glen.4'                 │       0        │  '0 (no invoices?)'  │
    └─────────┴──────────────┴──────────────────────────────────────────┴────────────────┴──────────────────────┘
    2021-02-11 12:21:01 [yajsapi] info: Total Cost: 0.002062975904072222
    ✨  Done in 426.28s.

yagnadatadir.zip

ederenn - Win 10 - js - 408 after computation is finished.
    2021-02-11 12:30:26 [yajsapi] debug: waitForApproval(3ded7fbbc00297e5d89e17f6eafbfbb91fe5708e81c677ee13ed3891cb573149) raised ApiException Error: Request failed with status code 408
    Done in 100.78s.

yagna_rCURRENT (5).log

yajsapi (2).log

"undefined offers have been collected from the market"

the warning message lacks the actual value and uses a value of undefined instead

2020-11-17 17:40:04 [yajsapi] warn: undefined offers have been collected from the market, but
no provider has responded for 20s. Make sure you're using the latest released versions of yagna and yajsapi, and the correct subnet.

[UX] Add user friendly warning for new added apis

If anyone tries to run updated yapapi/yajsapi from source (or on upcoming release when released) with yagna < 0.5.0, they will have 404 status code for new payment api.

What I propose instead, it's better to indicate "to be able to use this version of sdk you need to update yagna to something above >= 0.5.0" warning, so user will know what needs to be done.

Graceful shutdown on interruption ( ctrl + c ) doesn't work as intended

$ yarn ts:blender
yarn run v1.22.5
$ ts-node-script ./blender/blender.ts
Using subnet: community.3
2020-11-19 12:59:37 [yajsapi] info: GFTP Version:0.1.2
2020-11-19 12:59:38 [yajsapi] info: Demand published on the market
2020-11-19 12:59:42 [yajsapi] info: Received proposals from 1 providers so far
2020-11-19 12:59:44 [yajsapi] info: Received proposals from 2 providers so far
2020-11-19 12:59:44 [yajsapi] info: Agreement proposed to provider 'manchester.3'
2020-11-19 12:59:44 [yajsapi] info: Received proposals from 3 providers so far
2020-11-19 12:59:44 [yajsapi] info: Received proposals from 4 providers so far
2020-11-19 12:59:44 [yajsapi] info: Received proposals from 5 providers so far
2020-11-19 12:59:46 [yajsapi] info: Received proposals from 6 providers so far
2020-11-19 12:59:52 [yajsapi] info: Agreement confirmed by provider 'manchester.3'
2020-11-19 12:59:52 [yajsapi] info: Task sent to provider 'manchester.3', task data: 50
2020-11-19 12:59:54 [yajsapi] info: Agreement proposed to provider 'ada'
2020-11-19 13:00:00 [yajsapi] info: Task computed by provider 'manchester.3', task data: 50
2020-11-19 13:00:00 [yajsapi] info: Task sent to provider 'manchester.3', task data: 40
result= output_50.png
^C
adam@IMAPP1006 ~/Downloads/golem-requestor-linux-pre-rel-v0.5.0-495172ea/yajsapi/examples (b0.2 u=)
$ Unhandled rejection undefined

2020-11-19 13:00:05 [yajsapi] error: fail= undefined

Move Payment tables in log, under another flag

Tables as a payment indicator is a nice feature however, I am in favor of moving them under a separate flag i.e. "-t" so developers won't have any problem parsing regular log of the library.

Lag on loggger

There is 0 > x >= 2 seconds lag due to dequeuing get method. Have better async dequeuing logic

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.