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

Implement .stat() function #159

Merged
merged 1 commit into from
Jan 30, 2018
Merged
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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,20 @@ Sets the API address.

* `value` should be a [Multiaddr](https://github.com/multiformats/js-multiaddr) or a String representing a valid one.

### `repo.stat ([options], callback)`

Gets the repo status.

`options` is an object which might contain the key `human`, which is a boolean indicating whether or not the `repoSize` should be displayed in MiB or not.

`callback` is a function with the signature `function (err, stats)`, where `stats` is an Object with the following keys:

- `numObjects`
- `repoPath`
- `repoSize`
- `version`
- `storageMax`

## Notes

- [Explanation of how repo is structured](https://github.com/ipfs/js-ipfs-repo/pull/111#issuecomment-279948247)
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,22 @@
"dependencies": {
"async": "^2.6.0",
"base32.js": "~0.1.0",
"big.js": "^5.0.3",
"cids": "~0.5.2",
"interface-datastore": "~0.4.2",
"datastore-core": "~0.4.0",
"datastore-fs": "~0.4.2",
"datastore-level": "~0.7.0",
"debug": "^3.1.0",
"interface-datastore": "~0.4.2",
"ipfs-block": "~0.6.1",
"level-js": "timkuijsten/level.js#idbunwrapper",
"leveldown": "^1.7.2",
"lock-me": "^1.0.3",
"lodash.get": "^4.4.2",
"lodash.has": "^4.5.2",
"lodash.set": "^4.3.2",
"multiaddr": "^3.0.1"
"multiaddr": "^3.0.1",
"pull-stream": "^3.6.1"
},
"license": "MIT",
"contributors": [
Expand Down
14 changes: 14 additions & 0 deletions src/blockstore.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const Block = require('ipfs-block')
const setImmediate = require('async/setImmediate')
const reject = require('async/reject')
const CID = require('cids')
const pull = require('pull-stream')

/**
* Transform a raw buffer to a base32 encoded key.
Expand Down Expand Up @@ -49,6 +50,19 @@ function maybeWithSharding (filestore, options, callback) {

function createBaseStore (store) {
return {
/**
* Query the store.
*
* @param {object} query
* @param {function(Error, Array)} callback
* @return {void}
*/
query (query, callback) {
pull(
store.query(query),
pull.collect(callback)
)
},
/**
* Get a single block by CID.
*
Expand Down
79 changes: 79 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const each = require('async/each')
const assert = require('assert')
const path = require('path')
const debug = require('debug')
const Big = require('big.js')
const pull = require('pull-stream')

const backends = require('./backends')
const version = require('./version')
Expand All @@ -17,6 +19,8 @@ const defaultOptions = require('./default-options')

const log = debug('repo')

const noLimit = Number.MAX_SAFE_INTEGER

const lockers = {
memory: require('./lock-memory'),
fs: require('./lock')
Expand Down Expand Up @@ -200,6 +204,81 @@ class IpfsRepo {
exists (callback) {
this.version.exists(callback)
}

/**
* Get repo status.
*
* @param {Object} options
* @param {Boolean} options.human
* @param {function(Error, Object)} callback
* @return {void}
*/
stat (options, callback) {
if (typeof options === 'function') {
callback = options
options = {}
}

options = Object.assign({}, {human: false}, options)

parallel({
storageMax: (cb) => this.config.get('Datastore.StorageMax', (err, max) => {
if (err) {
cb(null, new Big(noLimit))
} else {
cb(null, new Big(max))
}
}),
version: (cb) => this.version.get(cb),
blocks: (cb) => this.blocks.query({}, (err, list) => {
list = list || []

const count = new Big(list.length)
let size = new Big(0)

list.forEach(block => {
size = size
.plus(block.value.byteLength)
.plus(block.key._buf.byteLength)
})

cb(err, {
count: count,
size: size
})
}),
datastore: (cb) => getSize(this.datastore, cb),
keys: (cb) => getSize(this.keys, cb)
}, (err, results) => {
if (err) return callback(err)

let size = results.blocks.size
.plus(results.datastore)
.plus(results.keys)

if (options.human) {
size = size.div(1048576)
}

callback(null, {
repoPath: this.path,
storageMax: results.storageMax,
version: results.version,
numObjects: results.blocks.count,
repoSize: size
})
})
}
}

function getSize (queryFn, callback) {
pull(
queryFn.query({}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to avoid buffering: I think you can reduce this iteratively instead of buffering the entire list.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My comment refers to line below where you do pull.collect.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but I didn't get this. How can I do this iteratively?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the query is a pull-stream, you can use pull.reduce like this:

pull(
  source,
  pull.reduce((val) => compute(val), 0, callback))

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, didn't know that. Thanks

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! 😄

pull.reduce((sum, block) => {
return sum
.plus(block.value.byteLength)
.plus(block.key._buf.byteLength)
}, new Big(0), callback))
}

module.exports = IpfsRepo
Expand Down
1 change: 1 addition & 0 deletions test/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ describe('IPFS Repo Tests onNode.js', () => {
require('./blockstore-test')(repo)
require('./datastore-test')(repo)
require('./keystore-test')(repo)
require('./stat-test')(repo)
if (!r.init) {
require('./interop-test')(repo)
}
Expand Down
46 changes: 46 additions & 0 deletions test/stat-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* eslint-env mocha */
'use strict'

const chai = require('chai')
chai.use(require('dirty-chai'))
const expect = chai.expect

module.exports = (repo) => {
describe('stat', () => {
it('get stats', (done) => {
repo.stat((err, stats) => {
expect(err).to.not.exist()
expect(stats).to.exist()
expect(stats).to.have.property('numObjects')
expect(stats).to.have.property('version')
expect(stats).to.have.property('repoPath')
expect(stats).to.have.property('repoSize')
expect(stats).to.have.property('storageMax')

expect(stats.numObjects > '0').to.eql(true)
expect(stats.version > '0').to.eql(true)
expect(stats.repoSize > '0').to.eql(true)
expect(stats.storageMax > '0').to.eql(true)
done()
})
})

it('get human stats', (done) => {
repo.stat({human: true}, (err, stats) => {
expect(err).to.not.exist()
expect(stats).to.exist()
expect(stats).to.have.property('numObjects')
expect(stats).to.have.property('version')
expect(stats).to.have.property('repoPath')
expect(stats).to.have.property('repoSize')
expect(stats).to.have.property('storageMax')

expect(stats.numObjects > '0').to.eql(true)
expect(stats.version > '0').to.eql(true)
expect(stats.repoSize > '0').to.eql(true)
expect(stats.storageMax > '0').to.eql(true)
done()
})
})
})
}